Domo地址
项目地址https://github.com/kinglong123/FloatImages
draghelper分支:
scroll 的使用
Scroller是一个专门用于处理滚动效果的工具类,可能在大多数情况下,我们直接使用Scroller的场景并不多,但是很多大家所熟知的控件在内部都是使用Scroller来实现的,如ViewPager、ListView等。而如果能够把Scroller的用法熟练掌握的话,我们自己也可以轻松实现出类似于ViewPager这样的功能。那么首先新建一个ScrollerTest项目,今天就让我们通过例子来学习一下吧。
先撇开Scroller类不谈,其实任何一个控件都是可以滚动的,因为在View类当中有scrollTo()和scrollBy()这两个方法。
对于scrollTo()和scrollBy()方法还有一点需要注意,这点也很重要,假如你给一个LinearLayout调用scrollTo()方法,并不是LinearLayout滚动,而是LinearLayout里面的内容进行滚动,比如你想对一个按钮进行滚动,直接用Button调用scrollTo()一定达不到你的需求,大家可以试一试,如果真要对某个按钮进行scrollTo()滚动的话,我们可以在Button外面包裹一层Layout,然后对Layout调用scrollTo()方法。
即:scrollTo()和scrollBy()方法特别注意:如果你给一个ViewGroup调用scrollTo()方法滚动的是ViewGroup里面的内容,如果想滚动一个ViewGroup则再给他嵌套一个外层,滚动外层即可。
scrollTo(int x, int y) x,y为正则向xy轴反方向移动,反之同理。
就是两个scroll方法中传入的参数,第一个参数x表示相对于当前位置横向移动的距离,
正值向左移动,负值向右移动
单位是像素。第二个参数y表示相对于当前位置纵向移动的距离,
正值向上移动,负值向下移动,单位是像素。
注意点:
1、即创建Scroller的实例,由于Scroller的实例只需创建一次,因此我们把它放到构造函数里面执行。另外在构建函数中我们还初始化的TouchSlop的值,这个值在后面将用于判断当前用户的操作是否是拖动。
2、接着重写onMeasure()方法和onLayout()方法,在onMeasure()方法中测量ScrollerLayout里的每一个子控件的大小,在onLayout()方法中为ScrollerLayout里的每一个子控件在水平方向上进行布局
3、接下来我们就该进行上述步骤中的第二步操作,调用startScroll()方法来初始化滚动数据并刷新界面。startScroll()方法接收四个参数,第一个参数是滚动开始时X的坐标,第二个参数是滚动开始时Y的坐标,第三个参数是横向滚动的距离,正值表示向左滚动,第四个参数是纵向滚动的距离,正值表示向上滚动。紧接着调用invalidate()方法来刷新界面。
4、在整个后续的平滑滚动过程中,computeScroll()方法是会一直被调用的,因此我们需要不断调用Scroller的computeScrollOffset()方法来进行判断滚动操作是否已经完成了,如果还没完成的话,那就继续调用scrollTo()方法,并把Scroller的curX和curY坐标传入,然后刷新界面从而完成平滑滚动的操作。
ViewDragHelper的使用
对于自定义ViewGroup而言这边的ViewDragHelper是一个很不错的实用程序类。它给我们提供一系列的方法和相关状态,让我们可以进行拖拽移动或者重新定位ViewGroup中子视图View。ViewDragHelper是一个简化View的拖拽操作的帮助类,使用起来比较简单与方便,一般我们只需要实现几个方法和一个CallBack类就可以实现拖动的View。
知识点:
1、ViewDragHelper 是一个工具类,为拖拽而生,它提供了一系列的方法和回调方法用来操纵拖拽及跟踪 child 被拖拽时的位置、状态。
2、回调方法 tryCaptureView() 返回值为 true 时,ViewDragHelper 才能拖动对应的 child。但是可以直接调用 captureChildView() 方法来指定被拖动的 child。
3、ViewDragHelper 要在 ViewGroup 中的 onInterceptTouchEvent() 方法中调用 shouldInterceptTouchEvent() 方法,然后在 ViewGroup 中的 onTouchEvent() 方法调用 processTouchEvent()。
4、ViewDragHelper 内部有一个 Scroller 变量,所以涉及到位移动画如 settleCapturedViewAt()、flingCapturedView()、smoothSlideViewTo() 方法时要复写 ViewGroup 的 computeScroll() 方法,在这个方法中调用 ViewDragHelper 的 continueSettling()。
5、如果要移动像 Button 这样 clickable == true 的控件,要复写 ViewDragHelper.Callback 中的两个回调方法 getViewHorizontalDragRange() 和 getViewVerticalDragRange(),使它们对应方法的返回值大于 0 就好了。
借助ViewDragHelper 实现相纵向滑动消失:
思路
今日头条使用了Activity,因为这个Activity可以显示前一个Activity,所以它一定是一个透明的Activity
这个用自定义的Activity主题就可以实现
用Android的Layout Inspector 工具查看今日头条它的布局,使用到ViewPager
1、定义FloatView继承LinearLayout
2、借助ViewDragHelper: mHelper = ViewDragHelper.create(this, 1f, new DragHelperCallback());
3、只拦截纵向滑动事件
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mHelper.cancel();
return false;
}
final float x = ev.getX();
final float y = ev.getY();
boolean interceptTap = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
mInitialMotionX = x;
mInitialMotionY = y;
// interceptTap = mDragHelper.isViewUnder(mHeaderView, (int) x, (int) y);
break;
}
case MotionEvent.ACTION_MOVE: {
final float adx = Math.abs(x - mInitialMotionX);
final float ady = Math.abs(y - mInitialMotionY);
final int slop = mHelper.getTouchSlop();
/*useless*/
if (adx > ady) {
mHelper.cancel();
return false;
}
}
}
return mHelper.shouldInterceptTouchEvent(ev) ;
}
4、重写onViewReleased方法,根据位置做出滑动。滑动没有超过一半,回到原位,超过直接划出
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel)
{
final int childWidth = getHeight()/2;
float offset = (releasedChild.getTop()) * 1.0f / childWidth;
if(yvel>0 && offset >0){
mHelper.settleCapturedViewAt((int)mDragOriLeft, getHeight());
}
else if(yvel >= 0 && offset > 0.5f){
mHelper.settleCapturedViewAt((int)mDragOriLeft, getHeight());
}
else if(yvel<0 && offset <0){
mHelper.settleCapturedViewAt((int)mDragOriLeft, -getHeight());
}
else if(yvel<=0 && offset < -0.5f){
mHelper.settleCapturedViewAt((int)mDragOriLeft, -getHeight());
}
else{
mHelper.settleCapturedViewAt((int)mDragOriLeft,(int)mDragOriTop);
}
invalidate();
}
5、重写computeScroll
@Override
public void computeScroll()
{
if (mHelper != null && mHelper.continueSettling(true)) {
invalidate();
}else {
if(Math.abs(mDragOffset) > 0.8 & mPositionListener!=null){
mPositionListener.onFlingOutFinish(); //滑出回调
}
}
}
6、使用:
ViewPager 放入FloatView布局中
floatView.setPositionListener(new FloatView.OnPositionChangeListener() {
@Override
public void onPositionChange(int initTop, int nowTop, float ratio) {
if(ratio <0.05){
ratio = 0;
}
float alpha = 1 - Math.min(1, ratio * 5);
mYfBottomLayout.setAlpha(alpha);
mYfTitleLayout.setAlpha(alpha);
mBackground.setAlpha(Math.max(0, 1 - ratio));//透明度设置
}
@Override
public void onFlingOutFinish() {
finish();//划出直接关闭activity
}
});