View滑动的方法是现实绚丽的自定义控件的基础
一般来说实现滑动的方法有三种:
- 通过View本身提供的scrollTo/scrollBy方法来实现
- 通过动画方法
- 通过更改View的LayoutParams,实现重新布局来实现。
一、通过View本身提供的scrollTo/scrollBy方法来实现
scrollTo/scrollBy改变的是控件的内容的而不是控件在布局中的位置。例如设置一个长宽高都是100dp的TextView,效果如下:
scrollTo使用方法是:
/**
* x:是在x轴上的偏移量
* y:是在y轴上的偏移量
* 例如从(0,0)偏移到(20,20),那么x=0-20=-20, y=0-20=-20
* 所以,如果从上向下,从左向右,那么偏移量x,y为负值,反之为正值。
*/
tv.scrollTo(int x,int y);
scrollBy可以多次偏移,再次偏移的时候就以上次偏移的位置作为起始点。具体效果如上图。
tv.scrollBy(int x,int y);
二、通过动画方式
动画的部分下次会详细描述,这次略过。
三、通过更改View的LayoutParams,实现重新布局来实现。
制作一个翻页的界面,在翻页的时候小圆点跟着变化,效果如下:
查看主要代码:
xml布局:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_test3"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.centran.zx_view_custom.Test3Activity">
<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp" >
<LinearLayout
android:id="@+id/ll_point_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
<View
android:id="@+id/view_red_point"
android:layout_width="10dp"
android:layout_height="10dp"
android:background="@drawable/shape_point_selected" />
</RelativeLayout>
</RelativeLayout>
重要代码片段:
/**
* 加载小圆点
*/
for(int i=0;i<imageId.length;i++){
View point = new View(this);
//设置圆点背景
point.setBackgroundResource(R.drawable.shape_point_normal);
//设置圆点宽高
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(30,30);
point.setLayoutParams(params);
if(i!=0){
params.leftMargin = 30;
}
llPointGroup.addView(point);
}
/**
* 获取两个圆点之间的距离
*/
//获取视图树
final ViewTreeObserver viewTreeObserver = llPointGroup.getViewTreeObserver();
//添加对试图树中layout过程的监控
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
System.out.println("layout");
llPointGroup.getViewTreeObserver().removeOnGlobalLayoutListener(this);
pointWidth = llPointGroup.getChildAt(1).getLeft() - llPointGroup.getChildAt(0).getLeft();
}
});
/**
* 监控ViewPager的滑动
*/
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
System.out.println("position::"+position+" positionOffset::"+positionOffset+" positionOffsetPixels::"+positionOffsetPixels);
int offset = (int) (pointWidth * positionOffset+pointWidth*position);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mRedPoint.getLayoutParams());
params.leftMargin = offset;
mRedPoint.setLayoutParams(params);
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
VelocityTracker介绍:
VelocityTracker是速度跟踪器,用于跟踪手指在滑动过程中的速度,包括竖直速度和水平速度。
//获取速度跟踪器的对象
velocityTracker = VelocityTracker.obtain();
@Override
public boolean onTouchEvent(MotionEvent event) {
//添加速度跟踪器监控的事件
velocityTracker.addMovement(event);
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
/**
* 设置速度跟踪器的时间段
* 1000:表示跟踪1s中滑过的像素
*/
velocityTracker.computeCurrentVelocity(1000);
xVelocity = velocityTracker.getXVelocity();
yVelocity = velocityTracker.getYVelocity();
break;
case MotionEvent.ACTION_UP:
Toast.makeText(Test4Activity.this, "水平速度::"+xVelocity+" 竖直速度::"+yVelocity, Toast.LENGTH_SHORT).show();
break;
default:break;
}
return super.onTouchEvent(event);
}
/**
* 当不使用的时候,重置以及释放内存
*/
velocityTracker.clear();
velocityTracker.recycle();
因为: 速度 = (终点位置 - 起点位置) / 时间段 , 所以速度有正负值,向下/向右滑动,水平/竖直的速度为正值,反之为负值。
GestureDector详解:
手势检测器,虽然android中有onTouch()方法,但是这个方法太简单了,如果需要处理一些复杂的手势,那么就需要用到gestureDector。
GestureDector这个类提供了两个接口和一个类。
- OnGestureListener接口
- OnDoubleTapListener接口
SimpleOnGestureListener,此类继承了上面两个接口的所有方法,需要哪个方法就实现哪个方法。
GestureDetector mGestureDetector = new GestureDetector(mGestureListener); private GestureDetector.OnGestureListener mGestureListener = new GestureDetector.OnGestureListener(){ ...... } @Override public boolean onTouch(View v, MotionEvent event) { return mGestureDetector.onTouchEvent(event); }
弹性滑动:
上面介绍的滑动方法都是生硬的滑动,这次介绍弹性滑动,弹性滑动差不多有三种方法:
- 使用Scroller
- 通过动画
- 通过延时策略
一、使用Scroller
scroller需要与computeScroll配合使用:
Scroller mScroller = new Scroller(context);
public void smoothScrollTo(int destX,int destY,int durationTime){
int scrollX = getScrollX();
int scrollY = getScrollY();
mScroller.startScroll(scrollX,scrollX,destX,destY,durationTime);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
当调用smoothScrollTo方法的时候,invalidate()方法会导致View重绘。当View重绘后会在draw方法中调用computeScroll,而computeScroll又会去从Scroller中获取当前的scrollX和scrollY;然后又通过scrollTo实现滑动。接着又调用postInvalidate方法来进行绘制,这次绘制又会导致computeScroll方法的调用(注意此时获取的当前的scrollX和scrollY是上次滑动后的位置),如此反复,直至整个滑动过程结束。
二、通过动画方式
动画的部分下次会详细描述,这次略过。
三、通过延时策略方式
private static final int MESSAGE_TAG = 1;
private int mCount = 0;
private int TOTAL_COUNT = 30;
private int DELAY_TIME = 33;
public Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == MESSAGE_TAG){
mCount++;
if(mCount<TOTAL_COUNT){
int scrollX = -300/TOTAL_COUNT;
btn_moving.scrollBy(scrollX,0);
mHandler.sendEmptyMessageDelayed(MESSAGE_TAG,DELAY_TIME);
}
}
}
};