标签:scroller viewgroup android 控件
class QSrollView extends ViewGroup { public final static String TAG = QSrollView.class.getSimpleName(); public final static int TOUCH_STATE_SROLLING = 1; // 当前在滑动状态 public final static int TOUCH_STATE_FLING = 2; // 当前fling状态 public final static int TOUCH_STATE_DEFALUT = 0; // 默认 private int mTouchState = TOUCH_STATE_DEFALUT; private int mTouchSlop = 0; // 当前滑动阀值 private int mLastMontionY; // 记录上次y的位置 Scroller mScroller; // 滑动辅助类 private int mTotalLength = 0; // 整个控件的长度 private int mMaxmumVelocity = 0; // Velocity的阀值 private VelocityTracker mVelocityTracker; // Velocity int mPointID = 0; // pointID public QSrollView(Context context) { super(context); init(); } private void init() { mScroller = new Scroller(getContext()); mTouchSlop = ViewConfiguration.getTouchSlop(); mMaxmumVelocity = ViewConfiguration.getMaximumFlingVelocity(); }
/** * 重写onMeasure方法计算 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int size = getChildCount(); final int parentWidthSize = MeasureSpec.getSize(widthMeasureSpec); final int paretnHeightSize = MeasureSpec.getSize(heightMeasureSpec); for (int i = 0; i < size; ++i) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams childLp = child.getLayoutParams(); final boolean childWidthWC = childLp.width == LayoutParams.WRAP_CONTENT; final boolean childHeightWC = childLp.height == LayoutParams.WRAP_CONTENT; int childWidthMeasureSpec; int childHeightMeasureSpec; if (child.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams childMarginLp = (MarginLayoutParams) childLp; childWidthMeasureSpec = childWidthWC ? MeasureSpec .makeMeasureSpec(parentWidthSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight() + childMarginLp.leftMargin + childMarginLp.rightMargin, childLp.width); childHeightMeasureSpec = childHeightWC ? MeasureSpec .makeMeasureSpec(paretnHeightSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom() + childMarginLp.topMargin + childMarginLp.bottomMargin, childMarginLp.height); } else { childWidthMeasureSpec = childWidthWC ? MeasureSpec .makeMeasureSpec(parentWidthSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), childLp.width); childHeightMeasureSpec = childHeightWC ? MeasureSpec .makeMeasureSpec(paretnHeightSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), childLp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
/*** * 重写layout方法 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childStartPostion = 0; mTotalLength = 0; final int count = getChildCount(); if (count == 0) { return; } childStartPostion = getPaddingTop(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child != null && child.getVisibility() != View.GONE) { LayoutParams lp = child.getLayoutParams(); final int childHeight = child.getMeasuredHeight(); int leftMargin = 0; int rightMargin = 0; int topMargin = 0; int bottomMargin = 0; if (lp instanceof MarginLayoutParams) { MarginLayoutParams mlp = (MarginLayoutParams) lp; leftMargin = mlp.leftMargin; rightMargin = mlp.rightMargin; topMargin = mlp.topMargin; bottomMargin = mlp.bottomMargin; } childStartPostion += topMargin; int startX = (getWidth() - leftMargin - rightMargin - child .getMeasuredWidth()) / 2 + leftMargin; child.layout(startX, childStartPostion, startX + child.getMeasuredWidth(), childStartPostion + childHeight); childStartPostion += (childHeight + bottomMargin); } } childStartPostion += getPaddingBottom(); mTotalLength = childStartPostion; }
/** * 事件拦截 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); // 表示已经开始滑动了,不需要走该Action_MOVE方法了(第一次时可能调用)。 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_DEFALUT)) { return true; } int y = (int) ev.getY(); switch (action) { case MotionEvent.ACTION_MOVE: final int xDiff = (int) Math.abs(mLastMontionY - y); // 超过了最小滑动距离 if (xDiff > mTouchSlop) { mTouchState = TOUCH_STATE_SROLLING; } break; case MotionEvent.ACTION_POINTER_DOWN: mPointID = ev.getPointerId(ev.getActionIndex()); // 记录当前pointID break; case MotionEvent.ACTION_DOWN: mLastMontionY = y; Log.e(TAG, mScroller.isFinished() + ""); if (!mScroller.isFinished()) // 当动画还没有结束的时候强制结束 { mScroller.abortAnimation(); mScroller.forceFinished(true); } mTouchState = TOUCH_STATE_DEFALUT; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mTouchState = TOUCH_STATE_DEFALUT; break; } Log.e(TAG, mTouchState + "====" + TOUCH_STATE_DEFALUT); return mTouchState != TOUCH_STATE_DEFALUT; }
@Override public boolean onTouchEvent(MotionEvent event) { int touchIndex = event.getActionIndex(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPointID = event.getPointerId(0); mLastMontionY = (int) event.getY();// 记录按下的点 break; case MotionEvent.ACTION_POINTER_DOWN: // 添加多点触控的处理 mPointID = event.getPointerId(touchIndex); mLastMontionY = (int) (event.getY(touchIndex) + 0.5f); // 记录按下的点 break; case MotionEvent.ACTION_MOVE: touchIndex = event.findPointerIndex(mPointID); if (touchIndex < 0) // 当前index小于0就返false继续接受下一次事件 return false; int detaY = (int) (mLastMontionY - event.getY(touchIndex)); // 计算滑动的距离 scrollBy(0, detaY); // 调用滑动函数 mLastMontionY = (int) event.getY(touchIndex); // 记录上一次按下的点 break; case MotionEvent.ACTION_UP: Log.d("edsheng", "Action UP"); mVelocityTracker.computeCurrentVelocity(1000); if (Math.abs(mVelocityTracker.getYVelocity()) > mMaxmumVelocity&&!checkIsBroad()) { mScroller.fling(getScrollX(), getScrollY(), 0,-(int) mVelocityTracker.getYVelocity(), 0, 0, 0, mTotalLength - getHeight()); } else { actionUP(); // 回弹效果 } mTouchState = TOUCH_STATE_DEFALUT; break; case MotionEvent.ACTION_POINTER_UP: // 添加多点触控的支持 if (event.getPointerId(touchIndex) == mPointID) { final int newIndex = touchIndex == 0 ? 1 : 0; mPointID = event.getPointerId(newIndex); mLastMontionY = (int) (event.getY(newIndex) + 0.5f); } break; case MotionEvent.ACTION_CANCEL: mTouchState = TOUCH_STATE_DEFALUT; break; default: break; } // super.onTouchEvent(event); return true; }
@Override public void scrollBy(int x, int y) { // 判断当前视图是否超过了顶部或者顶部就让它滑动的距离为1/3这样就有越拉越拉不动的效果 if (getScrollY() < 0 || getScrollY() + getHeight() > mTotalLength) { super.scrollBy(x, y / 3); } else { super.scrollBy(x, y); } }
/** * 回弹函数 */ private void actionUP() { if (getScrollY() < 0 || getHeight() > mTotalLength) // 顶部回弹 { Log.d("edsheng", "顶部回弹!!!!"); mScroller.startScroll(0, getScrollY(), 0, -getScrollY()); // 开启回弹效果 invalidate(); } else if (getScrollY() + getHeight() > mTotalLength) // 底部回弹 { // 开启底部回弹 mScroller.startScroll(0, getScrollY(), 0, -(getScrollY() + getHeight() - mTotalLength)); invalidate(); } }
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) // 计算当前位置 { // 滚动 scrollTo(0, mScroller.getCurrY()); postInvalidate(); } }
package com.example.scolview; import android.content.Context; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.ListView; import android.widget.ScrollView; import android.widget.Scroller; import java.io.InputStream; /* * @FileName:QSrollView.java * @Version:V1.0 * @Date: 2015-2-1 Create * @author: edsheng * */ class QSrollView extends ViewGroup { public final static String TAG = QSrollView.class.getSimpleName(); public final static int TOUCH_STATE_SROLLING = 1; // 当前在滑动状态 public final static int TOUCH_STATE_FLING = 2; // 当前fling状态 public final static int TOUCH_STATE_DEFALUT = 0; // 默认 private int mTouchState = TOUCH_STATE_DEFALUT; private int mTouchSlop = 0; // 当前滑动阀值 private int mLastMontionY; // 记录上次y的位置 Scroller mScroller; // 滑动辅助类 private int mTotalLength = 0; // 整个控件的长度 private int mMaxmumVelocity = 0; // Velocity的阀值 private VelocityTracker mVelocityTracker; // Velocity int mPointID = 0; // pointID public QSrollView(Context context) { super(context); init(); } private void init() { mScroller = new Scroller(getContext()); mTouchSlop = ViewConfiguration.getTouchSlop(); mMaxmumVelocity = ViewConfiguration.getMaximumFlingVelocity(); } @Override public void scrollBy(int x, int y) { // 判断当前视图是否超过了顶部或者顶部就让它滑动的距离为1/3这样就有越拉越拉不动的效果 if (getScrollY() < 0 || getScrollY() + getHeight() > mTotalLength) { super.scrollBy(x, y / 3); } else { super.scrollBy(x, y); } } /** * 事件拦截 */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final int action = ev.getAction(); // 表示已经开始滑动了,不需要走该Action_MOVE方法了(第一次时可能调用)。 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_DEFALUT)) { return true; } int y = (int) ev.getY(); switch (action) { case MotionEvent.ACTION_MOVE: final int xDiff = (int) Math.abs(mLastMontionY - y); // 超过了最小滑动距离 if (xDiff > mTouchSlop) { mTouchState = TOUCH_STATE_SROLLING; } break; case MotionEvent.ACTION_POINTER_DOWN: mPointID = ev.getPointerId(ev.getActionIndex()); // 记录当前pointID break; case MotionEvent.ACTION_DOWN: mLastMontionY = y; Log.e(TAG, mScroller.isFinished() + ""); if (!mScroller.isFinished()) // 当动画还没有结束的时候强制结束 { mScroller.abortAnimation(); mScroller.forceFinished(true); } mTouchState = TOUCH_STATE_DEFALUT; break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mTouchState = TOUCH_STATE_DEFALUT; break; } Log.e(TAG, mTouchState + "====" + TOUCH_STATE_DEFALUT); return mTouchState != TOUCH_STATE_DEFALUT; } @Override public boolean onTouchEvent(MotionEvent event) { int touchIndex = event.getActionIndex(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPointID = event.getPointerId(0); mLastMontionY = (int) event.getY();// 记录按下的点 break; case MotionEvent.ACTION_POINTER_DOWN: // 添加多点触控的处理 mPointID = event.getPointerId(touchIndex); mLastMontionY = (int) (event.getY(touchIndex) + 0.5f); // 记录按下的点 break; case MotionEvent.ACTION_MOVE: touchIndex = event.findPointerIndex(mPointID); if (touchIndex < 0) // 当前index小于0就返false继续接受下一次事件 return false; int detaY = (int) (mLastMontionY - event.getY(touchIndex)); // 计算滑动的距离 scrollBy(0, detaY); // 调用滑动函数 mLastMontionY = (int) event.getY(touchIndex); // 记录上一次按下的点 break; case MotionEvent.ACTION_UP: Log.d("edsheng", "Action UP"); mVelocityTracker.computeCurrentVelocity(1000); if (Math.abs(mVelocityTracker.getYVelocity()) > mMaxmumVelocity&&!checkIsBroad()) { mScroller.fling(getScrollX(), getScrollY(), 0,-(int) mVelocityTracker.getYVelocity(), 0, 0, 0, mTotalLength - getHeight()); } else { actionUP(); // 回弹效果 } mTouchState = TOUCH_STATE_DEFALUT; break; case MotionEvent.ACTION_POINTER_UP: // 添加多点触控的支持 if (event.getPointerId(touchIndex) == mPointID) { final int newIndex = touchIndex == 0 ? 1 : 0; mPointID = event.getPointerId(newIndex); mLastMontionY = (int) (event.getY(newIndex) + 0.5f); } break; case MotionEvent.ACTION_CANCEL: mTouchState = TOUCH_STATE_DEFALUT; break; default: break; } // super.onTouchEvent(event); return true; } /** * 回弹函数 */ private void actionUP() { if (getScrollY() < 0 || getHeight() > mTotalLength) // 顶部回弹 { Log.d("edsheng", "顶部回弹!!!!"); mScroller.startScroll(0, getScrollY(), 0, -getScrollY()); // 开启回弹效果 invalidate(); } else if (getScrollY() + getHeight() > mTotalLength) // 底部回弹 { // 开启底部回弹 mScroller.startScroll(0, getScrollY(), 0, -(getScrollY() + getHeight() - mTotalLength)); invalidate(); } } /*** * 检测当前是否可回弹 * * @return */ boolean checkIsBroad() { if (getScrollY() < 0 || getScrollY() + getHeight() > mTotalLength) // 顶部回弹) // //顶部回弹 return true; else return false; } /** * 重写onMeasure方法计算 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int size = getChildCount(); final int parentWidthSize = MeasureSpec.getSize(widthMeasureSpec); final int paretnHeightSize = MeasureSpec.getSize(heightMeasureSpec); for (int i = 0; i < size; ++i) { final View child = getChildAt(i); if (child.getVisibility() != GONE) { LayoutParams childLp = child.getLayoutParams(); final boolean childWidthWC = childLp.width == LayoutParams.WRAP_CONTENT; final boolean childHeightWC = childLp.height == LayoutParams.WRAP_CONTENT; int childWidthMeasureSpec; int childHeightMeasureSpec; if (child.getLayoutParams() instanceof MarginLayoutParams) { MarginLayoutParams childMarginLp = (MarginLayoutParams) childLp; childWidthMeasureSpec = childWidthWC ? MeasureSpec .makeMeasureSpec(parentWidthSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight() + childMarginLp.leftMargin + childMarginLp.rightMargin, childLp.width); childHeightMeasureSpec = childHeightWC ? MeasureSpec .makeMeasureSpec(paretnHeightSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom() + childMarginLp.topMargin + childMarginLp.bottomMargin, childMarginLp.height); } else { childWidthMeasureSpec = childWidthWC ? MeasureSpec .makeMeasureSpec(parentWidthSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(widthMeasureSpec, getPaddingLeft() + getPaddingRight(), childLp.width); childHeightMeasureSpec = childHeightWC ? MeasureSpec .makeMeasureSpec(paretnHeightSize, MeasureSpec.UNSPECIFIED) : getChildMeasureSpec(heightMeasureSpec, getPaddingTop() + getPaddingBottom(), childLp.height); } child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } /*** * 重写layout方法 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childStartPostion = 0; mTotalLength = 0; final int count = getChildCount(); if (count == 0) { return; } childStartPostion = getPaddingTop(); for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child != null && child.getVisibility() != View.GONE) { LayoutParams lp = child.getLayoutParams(); final int childHeight = child.getMeasuredHeight(); int leftMargin = 0; int rightMargin = 0; int topMargin = 0; int bottomMargin = 0; if (lp instanceof MarginLayoutParams) { MarginLayoutParams mlp = (MarginLayoutParams) lp; leftMargin = mlp.leftMargin; rightMargin = mlp.rightMargin; topMargin = mlp.topMargin; bottomMargin = mlp.bottomMargin; } childStartPostion += topMargin; int startX = (getWidth() - leftMargin - rightMargin - child .getMeasuredWidth()) / 2 + leftMargin; child.layout(startX, childStartPostion, startX + child.getMeasuredWidth(), childStartPostion + childHeight); childStartPostion += (childHeight + bottomMargin); } } childStartPostion += getPaddingBottom(); mTotalLength = childStartPostion; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) // 计算当前位置 { // 滚动 scrollTo(0, mScroller.getCurrY()); postInvalidate(); } } }
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); QSrollView qSrollView = new QSrollView(this); // qbSrollView.setBackgroundColor(Color.RED); qSrollView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); LinearLayout.LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); layoutParams.bottomMargin = 20; LayoutParams commomlayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); commomlayoutParams.bottomMargin = 20; StyleButton button = new StyleButton(this); button.setText("按钮"); qSrollView.addView(button,commomlayoutParams); StyleButton button2 = new StyleButton(this); button2.setText("按钮"); button2.setRadius(16); qSrollView.addView(button2,commomlayoutParams); StyleButton button3 = new StyleButton(this); button3.setText("按钮"); button3.setRadius(32); button3.setTextNormalPressedcolor(Color.CYAN, Color.WHITE); button3.setBgNormalPressedcolor(Color.BLUE, Color.CYAN); qSrollView.addView(button3,commomlayoutParams); // for(int i= 0 ; i < 100; i ++) { StyleButton button4 = new StyleButton(this); // button4.setText("按钮"+i); button4.setRadius(80); button4.setBgNormalPressedcolor(Color.GRAY, Color.CYAN); button4.setTextNormalPressedcolor(Color.RED, Color.WHITE); qSrollView.addView(button4,commomlayoutParams); } StyleButton button5 = new StyleButton(this); button5 = new StyleButton(this); button5.setText("按钮"); button5.setRadius(50); button5.setTextNormalPressedcolor(Color.BLACK, Color.BLUE); button5.setBgNormalPressedcolor(Color.WHITE, Color.CYAN); qSrollView.addView(button5,commomlayoutParams); StyleButton button6 = new StyleButton(this); button6.setText("按钮"); button6.setRadius(50); button6.setTextNormalPressedcolor(Color.BLACK, Color.CYAN); button6.setBgNormalPressedcolor(Color.CYAN, Color.BLUE); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); params.leftMargin = 80; params.rightMargin = 80; qSrollView.addView(button6,commomlayoutParams); StyleButton button7 = new StyleButton(this); button7.setText("按钮"); button7.setRadius(80); qSrollView.addView(button7,commomlayoutParams); setContentView(qSrollView); }; }
android自定义控件系列教程----继承ViewGroup实现带阻力效果的可回弹的SrollView
标签:scroller viewgroup android 控件
原文地址:http://blog.csdn.net/codebob/article/details/43483675