https://github.com/wanglianghai/CustomViewGroup2
参考自:http://www.cherylgood.cn/articles/2017/06/01/1496287519464.html
scrollby()移动屏幕,正:屏幕左移,负屏幕右移(其实是移动view group)
滑动要给要点击事件的view开启可以点击
//点击事件要设置
childView.setClickable(true);
如果要支持recycler view请在case MotionEvent.ACTION_UP:上加
case MotionEvent.ACTION_UP:
reycler view默认水平滑动是会传给子view,垂直滑动是不会传给子view给自己的recycle view,action_cancel是动作取消了 invalidate后事件的传递
invalidate();
//通知View重绘-invalidate()->onDraw()->computeScroll()
高度是
childView.getMeasuredHeight();
不是childView.getHeight();
item的布局不要包含在其他的视图中(如LinearLayout)拦截事件会影响
return true;事件被消耗了结束
0.布局
<com.bignerdranch.android.scrollerdemo.SlideViewGroup
android:layout_width="match_parent"//给定屏幕宽度后面的第一个子view(match_parent)也会是屏幕宽度
android:layout_height="70dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"/>
<TextView
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="@color/colorAccent"/>
</com.bignerdranch.android.scrollerdemo.SlideViewGroup>
1.测量view group 占的屏幕
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = 0, height = 0, childCount;
childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
//点击事件要设置
childView.setClickable(true);
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
width += childView.getMeasuredWidth();
if (i == 1) {
mHideViewWidth = childView.getMeasuredWidth();
}
height = childView.getHeight();
}
setMeasuredDimension(width, height);
}
2.摆放里面的子视图
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int left = 0, top = 0;
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
childView.layout(left, top, left += childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
}
3.设置拦截器(也是解决滑动冲突的) 在前面通过系统提供的触摸值来确定是否自己解决还是下传给子view解决,默认给子view
private void init(Context context) {
//创建辅助类对象
//用户需滑动的最小的距离才被认为是滑动了,单位像素
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
mScaledTouchSlop = viewConfiguration.getScaledTouchSlop();
mScroller = new Scroller(context);
}
//Return true to steal motion events from the children and have
// them dispatched to this ViewGroup through onTouchEvent().
//判断滑动距离
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = ev.getRawX();
firstX = ev.getRawX();
break;
case MotionEvent.ACTION_MOVE:
//对左边界进行处理
// 当手指拖动值大于mScaledTouchSlop值时,认为应该进行滚动,拦截子控件的事件
if (Math.abs(ev.getRawX() - lastX) > mScaledTouchSlop) {
// Log.i(TAG, "onInterceptTouchEvent: ");
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
4.确定滑动可行后的动作
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//小于0向左
float distance = event.getRawX() - lastX;
lastX = event.getRawX();
scrollBy((int) -distance, 0);
break;
case MotionEvent.ACTION_UP:
//getScrollX()左屏幕边界为原点,左正右负,用系统的,直接算的有误差
//而且用边界算方便
//对右边界进行处理,不让其滑出
if (getScrollX() <= 0) {
scrollBy(-getScrollX(), 0);//1
} else {
Log.i(TAG, "onTouchEvent: " + getScrollX() + " " + mHideViewWidth);
if (getScrollX() < mHideViewWidth / 3) {
//删除按钮滑出区域小于1/3,滑回原来的位置
scrollBy(-getScrollX(), 0);
} else {
//删除按钮滑出区域大于1/3,滑出删除按钮
scrollBy((int) (mHideViewWidth - getScrollX()), 0);
}
}
break;
}
return super.onTouchEvent(event);
}
5.如果要添加动画,修改4的//1
private Scroller mScroller;//*1
mScroller = new Scroller(context);
mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0);//1
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
*1:/** *
This class encapsulates scrolling. You can use scrollers ({@link Scroller} * 这个类封装了(在滚动中)。 * or {@link OverScroller}) to collect the data you need to produce a scrolling * animation— 你能用滚动者(Scroller or OverScroller)去收集你需要提生成滚动动画的数据; for example, in response to a fling gesture. Scrollers track 一个列子,去响应一个迅猛的动作。scrollers追踪滑动的偏移量 * scroll offsets for you over time, but they don’t automatically 随着你的时间流逝,但是他不会自动应用这些位置给你的视图。 apply those * positions to your view. It’s your responsibility to get and apply new * 那是你的任务去得到和应用新的坐标以一个速率让这滑动动画看起来更平滑 * coordinates at a rate that will make the scrolling animation look smooth.
* *Here is a simple example:
* *private Scroller mScroller = new Scroller(context); * ... * public void zoomIn() { * // Revert any animation currently in progress * 恢复任何一个动画在进程中(让动画回到开始) * mScroller.forceFinished(true); * // Start scrolling by providing a starting point and * // the distance to travel * //开始滑动动画用提供的开始点和距离去进行 * mScroller.startScroll(0, 0, 100, 0); * // Invalidate to request a redraw * //无效请求去重绘 * invalidate(); * }* *
To track the changing positions of the x/y coordinates, use * {@link #computeScrollOffset}. The method returns a boolean to indicate * //去追踪改变的x/y的坐标位置,用computScrollOffset。这个方法将放回一个布尔值去指示scroller(动画)是否完成 * whether the scroller is finished. If it isn’t, it means that a fling or * programmatic pan operation is still in progress. You can use this method to * 如果没有完成,那表示一个抛或者程序操作还在进行。 * find the current offsets of the x and y coordinates, for example: 你能用这方法去找出当前的x和y坐标的偏移量
*
*
if (mScroller.computeScrollOffset()) {
* // Get current x and y positions
* int currX = mScroller.getCurrX();
* int currY = mScroller.getCurrY();
* ...
* }
*/
6.应用滑动力速率
private VelocityTracker tracker;
tracker = VelocityTracker.obtain();
mVelocity.addMovement(event);
mVelocity.computeCurrentVelocity(1000);
if ((getScrollX() < mHideViewWidth / 3 || velocity > 100) && velocity > -100)
/**
* Helper for tracking the velocity of touch events, for implementing
* flinging and other such gestures.
*
* Use {@link #obtain} to retrieve a new instance of the class when you are going
* to begin tracking. Put the motion events you receive into it with
* {@link #addMovement(MotionEvent)}. When you want to determine the velocity call
* {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
* and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id.
*/
7.支持recycleview //1添加
@Override
public boolean onTouchEvent(MotionEvent event) {
。。。
case MotionEvent.ACTION_CANCEL://1
case MotionEvent.ACTION_UP:
。。。
定义一个静态对象保存上一个item对象
private static SlideViewGroup mAdvanceSlide;//1
move时关闭上一个对象
case MotionEvent.ACTION_MOVE:
if (mAdvanceSlide != null) {//1
mAdvanceSlide.close();//1
}//1
cancel和up保存上一个对象
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mAdvanceSlide = this; //1
private void close() {
mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0);
invalidate();
}
8.给recycle view添加拦截器下滑超150才下滑其他的传给子view
private float downY;
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
downY = e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(downY - e.getRawY()) < 150) {
return false;
}
}
return super.onInterceptTouchEvent(e);
}