1.自定义控件
import android.content.Context;import android.util.AttributeSet;import android.util.DisplayMetrics;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.widget.Scroller;/** * Created by Administrator on 2019/2/26 */public class HorizontalScrollLayout extends ViewGroup { /** * 屏幕的宽度 */ private int mScreenWidth; /** * 手指按下时的getScrollX */ private int mScrollStart; /** * 手指抬起时的getScrollX */ private int mScrollEnd; /** * 记录移动时的X */ private int mLastX; /** * 滚动的辅助类 */ private Scroller mScroller; /** * 是否正在滚动 */ private boolean isScrolling; /** * 加速度检测 */ private VelocityTracker mVelocityTracker; /** * 记录当前页 */ private int currentPage = 0; /** * 子控件个数 */ private int childCount = 0; private OnPageChangeListener mOnPageChangeListener;// public HorizontalScrollLayout(Context context) {// super(context);// init(context);// } public HorizontalScrollLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); }// public HorizontalScrollLayout(Context context, AttributeSet attrs, int defStyleAttr) {// super(context, attrs, defStyleAttr);// init(context);// } private void init(Context context) { /** * 获得屏幕的宽度 */ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); mScreenWidth = outMetrics.widthPixels; //selectItem(0); // 初始化 mScroller = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //Log.i("order", "order:onMeasure"); childCount = getChildCount(); //Log.i("test", "onMeasure childCount = " + childCount); for (int i = 0; i < childCount; ++i) { View childView = getChildAt(i); measureChild(childView, mScreenWidth, heightMeasureSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //Log.i("order", "order:onLayout"); if (changed) { //Log.i(HorizontalScrollLayout.class.getName(), "changed == true"); //int childCount = getChildCount(); //Log.i("test", "onLayout childCount = " + childCount); // 设置主布局的高度 MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams(); lp.width = mScreenWidth * childCount; setLayoutParams(lp); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child.getVisibility() != View.GONE) { child.layout(i * mScreenWidth, t, (i + 1) * mScreenWidth, b);// 调用每个自布局的layout } } } } @Override public boolean onTouchEvent(MotionEvent event) { //Log.i("order", "order:onTouchEvent"); // 如果当前正在滚动,调用父类的onTouchEvent if (isScrolling) { //Log.i(HorizontalScrollLayout.class.getName(), "onTouchEvent isScrolling"); return super.onTouchEvent(event); } int action = event.getAction(); int x = (int) event.getX(); //Log.i(HorizontalScrollLayout.class.getName(), "onTouchEvent x = " + x); obtainVelocity(event); switch (action) { case MotionEvent.ACTION_DOWN: mScrollStart = getScrollX(); //Log.i("ScrollTag", "ACTION_DOWN mScrollStart = getScrollX() = " + mScrollStart); mLastX = x; //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_DOWN mLastX = " + mLastX); break; case MotionEvent.ACTION_MOVE: if (!mScroller.isFinished()) { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE !mScroller.isFinished()"); mScroller.abortAnimation(); } int dx = mLastX - x;// //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE last dx = " + dx); // 边界值检查 int scrollX = getScrollX(); // 已经到达左端,右拉多少,就往左滚动多少 if (dx < 0 && scrollX == 0) { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE (dx < 0 && scrollX < 0), dx = " + dx + ", scrollX = " + scrollX); dx = 0; } // 已经到达右部,左拉多少,就往右滚动多少// if (dx > 0 && scrollX + dx > getWidth() - mScreenWidth) { //Log.i("test", "getWidth() = " + getWidth() + ", mScreenWidth = " + mScreenWidth); if (dx > 0 && scrollX == mScreenWidth * (childCount -1)) { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE (dx > 0 && scrollX > mScreenWidth * childCount), dx = " + dx + ", scrollX = " + scrollX + ", mScreenWidth * childCount = " + mScreenWidth * childCount);// dx = getWidth() - mScreenWidth - scrollX; dx = 0; } //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE dx = " + dx); scrollBy(dx, 0); mLastX = x; //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_MOVE mLastX = " + mLastX); break; case MotionEvent.ACTION_UP: mScrollEnd = getScrollX(); //Log.i("ScrollTag", "ACTION_UP mScrollEnd = getScrollX() = " + mScrollEnd); int dScrollX = mScrollEnd - mScrollStart; //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP dScrollX = " + dScrollX); if (wantScrollToNext())// 往左滑动 { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP wantScrollToNext"); if (shouldScrollToNext()) { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP shouldScrollToNext"); mScroller.startScroll(getScrollX(), 0, mScreenWidth - dScrollX, 0); } else { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP shouldScrollToNext false"); mScroller.startScroll(getScrollX(), 0, -dScrollX, 0); } } if (wantScrollToPre())// 往右滑动 { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP wantScrollToPre"); if (shouldScrollToPre()) { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP shouldScrollToPre"); mScroller.startScroll(getScrollX(), 0, -mScreenWidth - dScrollX, 0); } else { //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP shouldScrollToPre false"); mScroller.startScroll(getScrollX(), 0, -dScrollX, 0); } } isScrolling = true; //Log.i(HorizontalScrollLayout.class.getName(), "ACTION_UP isScrolling = true"); postInvalidate(); recycleVelocity(); break; } return true; } /** * 初始化加速度检测器 * * @param event */ private void obtainVelocity(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 根据用户滑动,判断用户的意图是否是滚动到下一页 * * @return */ private boolean wantScrollToNext() { return mScrollEnd > mScrollStart; } /** * 根据滚动距离判断是否能够滚动到下一页 * * @return */ private boolean shouldScrollToNext() { return mScrollEnd - mScrollStart > mScreenWidth / 2 || Math.abs(getVelocity()) > 600; } /** * 根据用户滑动,判断用户的意图是否是滚动到上一页 * * @return */ private boolean wantScrollToPre() { return mScrollEnd < mScrollStart; } /** * 根据滚动距离判断是否能够滚动到上一页 * * @return */ private boolean shouldScrollToPre() { return -mScrollEnd + mScrollStart > mScreenWidth / 2 || Math.abs(getVelocity()) > 600; } /** * 获取x方向的加速度 * * @return */ private int getVelocity() { mVelocityTracker.computeCurrentVelocity(1000); return (int) mVelocityTracker.getXVelocity(); } @Override public void computeScroll() { super.computeScroll(); if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), 0); postInvalidate(); } else { int position = getScrollX() / mScreenWidth; //Log.i("xxx", position + "," + currentPage); if (position != currentPage) { if (mOnPageChangeListener != null) { currentPage = position; mOnPageChangeListener.onPageChange(currentPage); } } isScrolling = false; //Log.i(HorizontalScrollLayout.class.getName(), "computeScroll isScrolling = false"); } } /** * 释放资源 */ private void recycleVelocity() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } /** * 设置回调接口 * * @param onPageChangeListener */ public void setOnPageChangeListener(OnPageChangeListener onPageChangeListener) { mOnPageChangeListener = onPageChangeListener; } /** * 回调接口 */ public interface OnPageChangeListener { void onPageChange(int currentPage); } /** * 设置选中某一页 */ public void selectItem(int index) { scrollTo(index * mScreenWidth, 0); invalidate(); }}
2.布局文件