View的弹性滑动
实现渐进式滑动的共同思想:将一次大的滑动分成若干次小的滑动,并在一个时间段内完成。
- Scroller
代码示例:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.frame_layout)
MyFrameLayout mFrameLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@OnClick(R.id.btn)
public void onViewClicked() {
mFrameLayout.smoothScrollTo(-200, 0);
}
}
MyFrameLayout代码
public class MyFrameLayout extends FrameLayout {
private Scroller mScroller;
public MyFrameLayout(Context context) {
super(context);
}
public MyFrameLayout( Context context, AttributeSet attrs) {
super(context, attrs);
if (mScroller == null){
mScroller = new Scroller(context);
}
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
/**
* <p>Cause an invalidate to happen on a subsequent cycle through the event loop.
* Use this to invalidate the View from a non-UI thread.</p>
*
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
* @see #invalidate()
* @see #postInvalidateDelayed(long)
*/
postInvalidate();
}
}
public void smoothScrollTo(int destX,int destY){
/**
* Return the scrolled left position of this view. This is the left edge of
* the displayed part of your view. You do not need to draw any pixels
* farther left, since those are outside of the frame of your view on
* screen.
*
* @return The left edge of the displayed part of your view, in pixels.
* getScrollX()
*/
int ScrollX=getScrollX();
int deltaX=destX-ScrollX;
mScroller.startScroll(ScrollX,0,deltaX,0,2000);
/**
* Invalidate the whole view. If the view is visible,
* {@link #onDraw(android.graphics.Canvas)} will be called at some point in
* the future.
* <p>
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*/
invalidate();
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/layout"
tools:context=".MainActivity">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="缓慢滚动到指定位置"
/>
<com.example.viewtest.MyFrameLayout
android:layout_marginTop="10dp"
android:id="@+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
</com.example.viewtest.MyFrameLayout>
</LinearLayout>
拓展:invalidate源码分析 https://cruise1008.github.io/2016/04/30/how-does-invalidate-and-requestLayout-work/
- 使用动画
根据动画在给定的时间内完成整个动画过程的依据,可以在动画的每一帧到来时获取动画完成的比例,再根据这个比例计算出当前View所要滑动的距离。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn)
Button mBtn;
private final int startX=0;
private final int deltaX=-100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1).setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animatedFraction = animation.getAnimatedFraction();
mBtn.scrollTo(startX+(int)(deltaX*animatedFraction),0);
}
});
valueAnimator.start();
}
}
- 使用延时策略
通过发送一系列延时消息从而达到一种渐进式的效果,可以使用Handler或View的postDelayed方法,也可以使用线程的sleep方法。
public class MainActivity extends AppCompatActivity {
@BindView(R.id.btn)
Button mBtn;
private static final int MESSAGE_SCROLL_TO=1;
private static final int FRAME_COUNT=30;
private static final int DELAYED_TIME=33;
private int mCount=0;
@SuppressLint("HandlerLeak")
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_SCROLL_TO:{
mCount++;
if (mCount <= FRAME_COUNT){
float fraction=mCount / (float) FRAME_COUNT;
int scrollX = (int) (fraction * 100);
mBtn.scrollTo(scrollX,0);
mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,DELAYED_TIME);
}
break;
}
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mHandler.sendEmptyMessageDelayed(MESSAGE_SCROLL_TO,DELAYED_TIME);
}
}
参考:1、 《Android 开发艺术探索》 任玉刚
2、 invalidate源码分析 Cruise Yang:https://cruise1008.github.io/2016/04/30/how-does-invalidate-and-requestLayout-work/
下一篇:View事件分发机制:https://blog.youkuaiyun.com/lwj_hewo/article/details/90544368