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.布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.zsby.myscrollviewgroup.customview.HorizontalScrollLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f0f"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0ff"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0f0"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00f"></TextView>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"></TextView>
</com.zsby.myscrollviewgroup.customview.HorizontalScrollLayout>