简介
最近比较闲,就多学习了下,关键是不看点东西,就犯困啊。墨迹天气这个应用有不少地方需要学习的,这篇文章呢, 说一下他的“我”Tab页下拉拉伸图片展示效果,如果留意的话, 像QQ的好友动态也有差不多的效果。
代码分析
代码比较简单了,就重写了一个ScrollView类,先说说他的原理吧,我是先根据id拿到这个ImageView,然后获得他的TopMargin也就是遮掩后的偏移值,在触摸的时候,对ImageView的TopMargin进行改变产生效果,松手的时候搞个属性动画让他还原到以前的位置就Ok了。好了。 原理就是这样了。
先看看布局xml吧
<com.wjj.imagepull.PullScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:background="@android:color/white" android:id="@+id/scroll_view" android:scrollbars="none" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/pull_img" android:layout_marginTop="-50dp" android:src="@drawable/personal_back" android:layout_width="match_parent" android:scaleType="fitXY" android:layout_height="250dp" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/feed_fengfeng" android:scaleType="fitXY" android:layout_centerInParent="true" /> </RelativeLayout> //此处省略...... </LinearLayout> </com.wjj.imagepull.PullScrollView>
大家看到这个设置了吧android:layout_marginTop="-50dp"这个就是要遮掩的多少了,还有这个android:id="@+id/pull_img" 这个id,别乱改了, 我就是根据这个来拿的这个ImageView的。
上代码了
public class PullScrollView extends ScrollView implements ViewTreeObserver.OnGlobalLayoutListener { View view; int srcTopMargion; float lastY; float offsetY; public PullScrollView(Context context, AttributeSet attrs) { super(context, attrs); ViewTreeObserver observer = getViewTreeObserver(); if (null != observer) observer.addOnGlobalLayoutListener(this); } @Override protected void onFinishInflate() { super.onFinishInflate(); view = findViewById(R.id.pull_img); } @Override public boolean onTouchEvent(MotionEvent ev) { int action = ev.getAction(); float y = ev.getY(); Log.d("onTouchEvent", "action=" + action + ",y=" + y); MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); switch (action) { case MotionEvent.ACTION_DOWN: lastY = y; break; case MotionEvent.ACTION_MOVE: //计算滑动y方向偏移值 offsetY = y - lastY; //向下移动 if (offsetY > 0) { //滑动到看到所有图片展示,交给原来的逻辑处理 if (params.topMargin == 0) { return super.onTouchEvent(ev); } //在不是下拉图片的时候,向下移动,交给原来的逻辑处理 if (getScrollY() != 0) { return super.onTouchEvent(ev); } //可以下拉图片的情况 params.topMargin += offsetY / 10; Log.d("onTouchEvent", "topMargin" + params.topMargin + ",lastY=" + lastY + ",y=" + y + ",offsetY" + offsetY); if (params.topMargin >= 0) { params.topMargin = 0; } view.setLayoutParams(params); invalidate(); } lastY = y; break; case MotionEvent.ACTION_UP: //不和原始margion偏移一样的时候 if (params.topMargin != -srcTopMargion) { Log.d("ACTION_UP", "moveY=" + (srcTopMargion + params.topMargin)); //滚动原始偏移值和现在偏移值之间的差值 eg:3~10 ObjectAnimator animator = ObjectAnimator.ofInt(this, "moveY", params.topMargin, -srcTopMargion); animator.setDuration(200); animator.setInterpolator(new LinearInterpolator()); animator.start(); } break; } return super.onTouchEvent(ev); } /** * 设置移动中的Y值 * * @param value */ public void setMoveY(int value) { MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); params.topMargin = value; Log.d("computeScroll", "topMargin=" + params.topMargin); view.setLayoutParams(params); invalidate(); } @Override public void onGlobalLayout() { MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); srcTopMargion = -params.topMargin; Log.d("srcTopMargion", "" + srcTopMargion); getViewTreeObserver() .removeGlobalOnLayoutListener(this); } }
在PullScrollView构造方法里面注册了OnGlobalLayoutListener来监听Tree树改变后的回调,用来获取ImageView的topMargin作为以后还原的目标值,在onFinishInflate里面获取这个view,view = findViewById(R.id.pull_img);
onTouchEvent就是我们的主战场了, 逻辑都在这了,先判断在ACTION_MOVE中,是不是向下滑动,如果是的话,有2种情况是需要排除的, 就是不进行topMargin改变的,是哪些呢?
第一个是topMargin都滑到0了, 就不要滑了吧。
第二个是在其他地方滑动的时候,举个例子吧,就是正常的往下滑的时候,不做处理。
除了这2种情况,下面就是topMargin的改变了,这里我加了一个offsetY / 10的除以10的阻值,就是防止滑的太快了, 这个你可以进行自定义的。
当我抬手的时候,假如说我的topMargin和以前的初始值不一样的时候,还记得吧,咱们在onGlobalLayout取得的那个值,需要做的就是启动一个动画平移滑动到上面了。
当自动滑动的时候,这里用了ObjectAnimator属性动画,其实当时用的是Scroll,为什么又不用他了呢,因为重写的computeScroll方法,如果在里面处理的话,会和自己的滚动有冲突,感兴趣的可以试试。
还有一个setMoveY方法,这个就是属性动画调用的那个方法了,根据线性插值计算后返回的值,然后在用于改变topMargin,直到恢复到刚开始的状态。
结束语
好了,这个项目就算完了,不过我只在我的小米手机上测试了一下,其他分辨率不知道会不会有问题,大家可以测测看。
Github地址
https://github.com/wu928320442/ImagePull