Scroller:手指滑动中比较重要的一个辅助类,辅助完成一些动画参数的计算等。看到Scroller你可能感到陌生,但我们每个人都用过它,因为viewpage,listvew等控件源码都会用到它。而本文主要实现一个简单的viewpage效果。听说先上效果图不被打:
首先介绍scroller的几个重要方法:
scrollTo(x,y) : 让view 滑动到初始位置(x,y)处。(字面意思To: 就是到的意思,到了就不会再滚动)
scrollBy(): 让view 滑动到相对于view当前位置(x,y)处。(字面意思By: 就是步的意思,一步一步的滚动)
getScrollX() : 获取view 相对于初始位置的 x 方向的滚动偏移量。
getScrollY() : 获取view 相对于初始位置的 y 方向的滚动偏移量。
下面看张动态图你就会明白scrollTo 和scrollBy的区别:
好了,下边开始上代码
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<lzh.myview.view.ScrollerView
android:id="@+id/slv"
android:layout_width="match_parent"
android:layout_height="200dp">
</lzh.myview.view.ScrollerView>
<LinearLayout
android:id="@+id/ll_point"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="10dp"
android:layout_height="wrap_content"></LinearLayout>
</FrameLayout>
ScrollerView为我们实现翻页效果的自定义view,LinearLayout为指示器小圆圈的布局。
public class ScrollerView extends ViewGroup {
private Scroller scroller; // 用于滚动操作的实例
private int mixTouchSlip; // 判断是否滑动的临界值
private float xDown; // 手指按下时,X 的屏幕坐标
private float xMove; // 移动时,X的屏幕坐标
private float xLastMove ; // 上一次 move事件的屏幕坐标
private int leftBorder; //界面可滚动的左边界
private int rightBorder;// 界面可滚动的右边界
private int count ; // 图片的数量
private int position ; // 当前选中的位置
ViewGroup.LayoutParams params;
private Context mContext;
private OnPayListener listener;
public ScrollerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
scroller = new Scroller(context);
mixTouchSlip =ViewConfiguration.get(context).getScaledPagingTouchSlop();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 为每一个子控件测量大小
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean b, int l, int i1, int i2, int i3) {
if (b) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = getChildAt(i);
// 子控件进行布局
childView.layout(i * childView.getMeasuredWidth(), 0, (i + 1) * childView.getMeasuredWidth(), childView.getMeasuredHeight());
}
// 初始化左右边界值
leftBorder = getChildAt(0).getLeft()-100;
rightBorder = getChildAt(getChildCount() - 1).getRight()+100;
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
xDown = ev.getRawX();
xLastMove = xDown;
break;
case MotionEvent.ACTION_MOVE:
xMove = ev.getRawX();
float diff = Math.abs(xMove - xDown);
xLastMove = xMove;
// 当手指拖动值大于TouchSlop值时,认为应该进行滚动,拦截子控件的事件
if (diff > mixTouchSlip) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
xMove = event.getRawX();
int scrolledX = (int) (xLastMove - xMove);
// getScrollX()
if (getScrollX() + scrolledX < leftBorder) {
scrollTo(leftBorder, 0);
return true;
} else if (getScrollX() + getWidth() + scrolledX > rightBorder) {
scrollTo(rightBorder - getWidth(), 0);
return true;
}
scrollBy(scrolledX, 0);
xLastMove = xMove;
break;
case MotionEvent.ACTION_UP:
// 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面
int targetIndex = (getScrollX() + getWidth() / 2) / getWidth();
position = targetIndex;
int dx = targetIndex * getWidth() - getScrollX();
// 调用startScroll()方法来初始化滚动数据
// startScroll()方法接收四个参数,第一个参数是滚动开始时X的坐标,第二个参数是滚动开始时Y的坐标,
// 第三个参数是横向滚动的距离,正值表示向左滚动,第四个参数是纵向滚动的距离,正值表示向上滚动。紧接着调用invalidate()方法来刷新界面。
scroller.startScroll(getScrollX(), 0, dx, 0);
invalidate();
if (listener!=null) listener.onGetPoint(position);
break;
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
// 重写computeScroll()方法,并在其内部完成平滑滚动的逻辑
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}
public void setCount( int mcount){
count = mcount;
if (params==null) params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
for (int i = 0;i< count;i++){
ImageView image = new ImageView(mContext);
image.setScaleType(ImageView.ScaleType.FIT_XY);
image.setImageResource(R.drawable.meizhi);
image.setLayoutParams(params);
addView(image);
}
invalidate();
}
public interface OnPayListener {
void onGetPoint(int pas);
}
public void setListener(OnPayListener mlistener){
this.listener = mlistener;
}
}
首先我们自定义viwe继承的是ViewGroup,在这里我运用setCount()方法指定图片数量,并用代码动态添加imageview,在onMeasure 里为每一个子控件测量大小,然后在onLayout 为每个子空间进行布局,沿X轴水平摆放,然后调用onInterceptTouchEvent();根据滑动距离是否满足条件进行事件拦截,然后在onTouchEvent()中进行事件消费,实现移动效果,这里只得注意的是: 在 onTouchEvent 的 MotionEvent.ACTION_DOWN 中 要 return true; 因为如果不消费onInterceptTouchEvent 传下来的down事件,默认也不会响应move 和 up 事件,所以onTouchEvent down的时候 return true。OnPayListener 接口回调实现每当我进行滑动我都可以拿到当前滚动到哪个界面的position,然后进行设置指示器。
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(20,20);
lparams.leftMargin = 10;
for (int i= 0 ;i< n;i++){
TextView textView = new TextView(this);
textView.setLayoutParams(lparams);
textView.setBackgroundResource(R.drawable.point);
linearLayout.addView(textView);
}
linearLayout.getChildAt(0).setSelected(true);
slv.setListener(new ScrollerView.OnPayListener() {
@Override
public void onGetPoint(int pas) {
for (int i = 0; i<n;i++){
if (i==pas){
linearLayout.getChildAt(pas).setSelected(true);
}else {
linearLayout.getChildAt(i).setSelected(false);
}
}
}
});
在activity里为linearLayout动态添加控件,并设置小圆圈的背景颜色。
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/pink"></item>
<item android:state_selected="false" android:drawable="@drawable/white"></item>
</selector>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/colorAccent"></solid>
</shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/white"></solid>
</shape>
地址:
csdn:http://download.csdn.net/download/qq_38367802/10172775
github:https://github.com/liuzhenhang/myview