任意方向滑动中间的那块View,上面和左边的滚动条将联动。还支持回弹效果。
先说说这个布局文件
总的思路是获取中间那个自定义View在x,y轴上滚动的偏移量,然后让上面和左边的滚动条滚动相同的偏移量就行。主要是如何实现中间这个自定义的View。
public class ReportScrollview extends LinearLayout {
private Scroller mscroller;
int slop;
VelocityTracker mVelocityTracker;
int screenW,screenH;
public int visibleW=0,visibleH=0;
public ReportScrollview(Context context) {
super(context);
init(context);
}
public ReportScrollview(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ReportScrollview(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
mscroller=new Scroller(context);
slop= ViewConfiguration.getTouchSlop();
screenW=context.getResources().getDisplayMetrics().widthPixels;
screenH=context.getResources().getDisplayMetrics().heightPixels;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
}
float lastx,lasty;
@Override
public boolean onTouchEvent(MotionEvent event) {
if ( mVelocityTracker == null ) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
lastx=event.getX();
lasty=event.getY();
break;
case MotionEvent.ACTION_MOVE:
float curx=event.getX();
float cury=event.getY();
float disx=curx-lastx;
float disy=cury-lasty;
if(Math.abs(disx)>slop|| Math.abs(disy)>slop){
lastx=curx;
lasty=cury;
scrollBy(-(int)disx,-(int)disy);//让视图顺着手指滑动的方向移动
}
break;
case MotionEvent.ACTION_UP:
mVelocityTracker.computeCurrentVelocity(1000,2000.0f);
int xVelocity = (int) mVelocityTracker.getXVelocity();
int yVelocity = (int) mVelocityTracker.getYVelocity();
if ( Math.abs(xVelocity) > 10
|| Math.abs(xVelocity) > 10 ) {
mscroller.fling(getScrollX(),getScrollY(),
-xVelocity,-yVelocity,0,getMeasuredWidth()-visibleW,0,getMeasuredHeight()-visibleH);//手指拿起后,让视图随着惯性移动,并设置好移动的最大最小范围
invalidate();
}
break;
}
return true;
}
public void startScrollBy(int dx,int dy) {
mscroller.forceFinished(true);
int startX = getScrollX();
int startY = getScrollY();
mscroller.startScroll(startX,startY,startX+dx,startY+dy);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if (mscroller.computeScrollOffset()) {
scrollTo(mscroller.getCurrX(),mscroller.getCurrY());
if (mscroller.getCurrX() == getScrollX()
&& mscroller.getCurrY() == getScrollY() ) {
postInvalidate();
}
}
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if(scrollChangedListener!=null){
//通过接口把这个View横纵坐标上的偏移量暴露出去
scrollChangedListener.onScrollChanged(l,t,oldl,oldt);
}
}
private OnScrollChangedListener scrollChangedListener;
public void setScrollChangedListener(OnScrollChangedListener scrollChangedListener) {
this.scrollChangedListener = scrollChangedListener;
}
public interface OnScrollChangedListener{
public void onScrollChanged(int l, int t, int oldl, int oldt);
}
}
中间这个View我是通过继承LinearLayout实现的,主要是重写onTouchEvent,computeScroll(),onScrollChanged(int l, int t, int oldl, int oldt)这几个方法,关键步骤已经注释了。