這次做一個SwitchView1,與上文的效果是一樣的,但是
1.跟隨手滑動使用scrollBy,
2. 鬆手後的彈性滑動使用scroller
需要使用下面的幾個知識點:
知識點
-
系統很多控件都是使用了scrollBy+scorller來實現滑動效果的,例如ViewPager就是如此
如果你想控制ViewPager翻頁的速度,可以通過反射給ViewPager設置一個自定義的Scroller,在這個自定義的Scroller裏設置你想要的翻頁時長(做輪播圖你會用到的,Banner這是我做的一個輪播圖的demo,封裝的不太好)
-
scrollBy(dx,dy)增量滑動,
-
scrollTo(x,y)直接滑動到指定座標處
-
在本例中,一個LinearLayout裏有一個ImageView滑塊,當你想讓ImageView滑塊向左側移動時,必須想兩件事,
- 誰來調用scrollBy方法
- dx,dy的正負
結論是:每次調用scrollBy,讓該子控件的父容器去調用scrollBy方法,然後方向取反即可
-
scorller的使用也是固定的
第一步:初始化Scroller
mScroller = new Scroller(getContext());
第二步:調用scrollBy去讓一個控件滑動
((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★這是一個普適的公式,誰要滑動,就找誰的父親去調用scrollBy,然後方向取反
第三步:重寫computeScroll方法,下面的寫法也是固定的
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
第四步:調用startScroll(int startX, int startY, int dx, int dy, int duration) 實現彈性滑動
startX、startY是當前的scrollX,scrollY,dx、dy是你要基於當前位置要移動多少(增量)
所以(dx=你目的地的scrollX-當前的scrollX),下面的代碼裏,目的地的scrollX是0,所以
dx= 0 - ((View) mImageView.getParent()).getScrollX(),
dy=0,因爲這個例子不需要豎向滑動
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
0 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
應用:
直接上代碼:
package com.view.custom.dosometest.view;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Scroller;
/**
* 描述當前版本功能
* scroller實現
*
* @Project: DoSomeTest
* @author: cjx
* @date: 2019-12-01 10:06 星期日
*/
public class SwitchView1 extends LinearLayout {
private ImageView mImageView;
private LayoutParams mLayoutParams;
private int mSliderHeight;
private int mSliderWidth;
private int mWidth;
private int mHeight;
private Scroller mScroller;
public SwitchView1(Context context) {
super(context);
init();
}
public SwitchView1(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SwitchView1(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setBackgroundColor(Color.GRAY);
mScroller = new Scroller(getContext());
post(new Runnable() {
@Override
public void run() {
addSlider();
}
});
}
private void addSlider() {
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
mSliderWidth = mWidth / 2;
mSliderHeight = mHeight;
mLayoutParams = new LayoutParams(mSliderWidth, mSliderHeight);
mImageView = new ImageView(getContext());
mImageView.setBackgroundColor(Color.CYAN);
mImageView.setLayoutParams(mLayoutParams);
addView(mImageView);
}
float mLastX;
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) (x - mLastX);
// 防止滑塊滑出邊界,矯正deltaX,這給地方打印出getScrollX()、deltaX的日誌仔細看,就寫對了,小邏輯有點繞
if (getScrollX() - deltaX <= -mWidth / 2) {
deltaX = getScrollX() + mWidth / 2;
} else if (getScrollX() - deltaX > 0) {
deltaX = getScrollX();
}
// SwitchView1是個LinearLayout,他的子控件是滑塊Slider,假設我們想讓滑塊往右側移動
// 那麼我們應該先找到滑塊的父容器,讓他去調用scrollBy方法,正好父容器就是SwitchView1,所以this.scrollBy()即可,
// ok,既然我們是讓父容器SwitchView1去scrollBy,那麼父容器往左側移動,滑塊才能看起來是往右側移動,所以要在deltaX前面加負號
// 總結:每次調用scrollBy,讓該子控件的父容器去調用scrollBy方法,然後方向取反即可
// (但是父容器scrollBy了,他的所有子View都會動,這是副作用,當你需要所有子View一起動,那就正好ok)
((View) mImageView.getParent()).scrollBy(-deltaX, 0);// ★這是一個普適的公式,誰要滑動,就找誰的父親去調用scrollBy,然後方向取反
//scrollBy(-deltaX, 0);//這句話在本例子裏和上句話是等效的,因爲滑塊的父親就是this
Log.e("qwe", getScrollX() + "scroll");
mLastX = x;
break;
case MotionEvent.ACTION_UP:
if (getScrollX() > -mSliderWidth / 2) {
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
0 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
// 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
// mScroller.startScroll(getScrollX(), getScrollY(), 0 - getScrollX(), 0, 300);
} else {
mScroller.startScroll(
((View) mImageView.getParent()).getScrollX(),
((View) mImageView.getParent()).getScrollY(),
-mWidth / 2 - ((View) mImageView.getParent()).getScrollX(),
0,
300);
// 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
// mScroller.startScroll(getScrollX(), getScrollY(), getScrollX(), 0, 300);
}
invalidate();
break;
}
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) mImageView.getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
// 本例子下面的語句與上面的等效,因爲((View) mImageView.getParent())就是this
// scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
}