这次做一个SwitchView1,与上文的效果是一样的,但是
1.跟随手滑动使用scrollBy,
2. 松手后的弹性滑动使用scroller
需要使用下面的几个知识点:
知识点
-
系统很多控件都是使用了scrollBy+scorller来实现滑动效果的,例如ViewPager就是如此
如果你想控制ViewPager翻页的速度,可以通过反射给ViewPager设置一个自定义的Scroller,在这个自定义的Scroller里设置你想要的翻页时长(做轮播图你会用到的,Banner这是我做的一个轮播图的demo,封装的不太好)
-
scrollBy(dx,dy)增量滑动,
-
scrollTo(x,y)直接滑动到指定座标处
-
在本例中,一个LinearLayout里有一个ImageView滑块,当你想让ImageView滑块向左侧移动时,必须想两件事,
- 谁来调用scrollBy方法
- dx,dy的正负
结论是:每次调用scrollBy,让该子控件的父容器去调用scrollBy方法,然后方向取反即可
-
scorller的使用也是固定的
第一步:初始化Scroller
mScroller = new Scroller(getContext());
第二步:调用scrollBy去让一个控件滑动
((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★这是一个普适的公式,谁要滑动,就找谁的父亲去调用scrollBy,然后方向取反
第三步:重写computeScroll方法,下面的写法也是固定的
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
第四步:调用startScroll(int startX, int startY, int dx, int dy, int duration) 实现弹性滑动
startX、startY是当前的scrollX,scrollY,dx、dy是你要基于当前位置要移动多少(增量)
所以(dx=你目的地的scrollX-当前的scrollX),下面的代码里,目的地的scrollX是0,所以
dx= 0 - ((View) mImageView.getParent()).getScrollX(),
dy=0,因为这个例子不需要竖向滑动
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
0 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
应用:
直接上代码:
package com.view.custom.dosometest.view;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
* 描述当前版本功能
* scroller实现
*
* @Project: DoSomeTest
* @author: cjx
* @date: 2019-12-01 10:06 星期日
*/
public class SwitchView1 extends LinearLayout {
private ImageView mImageView;
private LayoutParams mLayoutParams;
private int mSliderHeight;
private int mSliderWidth;
private int mWidth;
private int mHeight;
private Scroller mScroller;
public SwitchView1(Context context) {
super(context);
init();
}
public SwitchView1(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SwitchView1(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setBackgroundColor(Color.GRAY);
mScroller = new Scroller(getContext());
post(new Runnable() {
@Override
public void run() {
addSlider();
}
});
}
private void addSlider() {
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
mSliderWidth = mWidth / 2;
mSliderHeight = mHeight;
mLayoutParams = new LayoutParams(mSliderWidth, mSliderHeight);
mImageView = new ImageView(getContext());
mImageView.setBackgroundColor(Color.CYAN);
mImageView.setLayoutParams(mLayoutParams);
addView(mImageView);
}
float mLastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (x - mLastX);
// 防止滑块滑出边界,矫正deltaX,这给地方打印出getScrollX()、deltaX的日志仔细看,就写对了,小逻辑有点绕
if (getScrollX() - deltaX <= -mWidth / 2) {
deltaX = getScrollX() + mWidth / 2;
} else if (getScrollX() - deltaX > 0) {
deltaX = getScrollX();
}
// SwitchView1是个LinearLayout,他的子控件是滑块Slider,假设我们想让滑块往右侧移动
// 那么我们应该先找到滑块的父容器,让他去调用scrollBy方法,正好父容器就是SwitchView1,所以this.scrollBy()即可,
// ok,既然我们是让父容器SwitchView1去scrollBy,那么父容器往左侧移动,滑块才能看起来是往右侧移动,所以要在deltaX前面加负号
// 总结:每次调用scrollBy,让该子控件的父容器去调用scrollBy方法,然后方向取反即可
// (但是父容器scrollBy了,他的所有子View都会动,这是副作用,当你需要所有子View一起动,那就正好ok)
((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★这是一个普适的公式,谁要滑动,就找谁的父亲去调用scrollBy,然后方向取反
//scrollBy(-deltaX, 0);//这句话在本例子里和上句话是等效的,因为滑块的父亲就是this
Log.e("qwe", getScrollX() + "scroll");
mLastX = x;
break;
case MotionEvent.ACTION_UP:
if (getScrollX() > -mSliderWidth / 2) {
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
0 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
// 本例子下面的语句与上面的等效,因为((View) mImageView.getParent())就是this
// mScroller.startScroll(getScrollX(), getScrollY(), 0 - getScrollX(), 0, 300);
} else {
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
-mWidth / 2 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
// 本例子下面的语句与上面的等效,因为((View) mImageView.getParent())就是this
// mScroller.startScroll(getScrollX(), getScrollY(), getScrollX(), 0, 300);
}
invalidate();
break;
}
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
// 本例子下面的语句与上面的等效,因为((View) mImageView.getParent())就是this
// scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}