<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"> <com.hopmet.meijiago.ui.widget.ScrollViewContainer android:layout_width="match_parent" android:layout_height="match_parent"> <include android:id="@+id/layout_good_detail_top" layout="@layout/include_good_detail_top" android:layout_width="match_parent" android:layout_height="match_parent"/> <include layout="@layout/include_good_detail_bottom" android:layout_width="match_parent" android:layout_height="match_parent"/> </com.hopmet.meijiago.ui.widget.ScrollViewContainer> </FrameLayout>
<?xml version="1.0" encoding="utf-8"?> <com.hopmet.meijiago.ui.widget.CustomScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="blocksDescendants" android:fillViewport="true" android:orientation="vertical" android:scrollbars="none"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v4.view.ViewPager android:id="@+id/vp_goods_detail" android:layout_width="match_parent" android:layout_height="320dp" android:flipInterval="1000" android:layout_gravity="center" android:persistentDrawingCache="animation" /> <com.hopmet.meijiago.ui.widget.IndicatorLinearLayout android:id="@+id/indicator_good_detail" android:layout_width="match_parent" android:layout_height="20dp" android:layout_gravity="bottom" android:gravity="center" /> </FrameLayout> </LinearLayout> </com.hopmet.meijiago.ui.widget.CustomScrollView>
<?xml version="1.0" encoding="utf-8"?> <com.hopmet.meijiago.ui.widget.CustomScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:scrollbars="none"> <com.hopmet.meijiago.ui.widget.MyWebView android:id="@+id/web_view_good_detail" android:layout_width="match_parent" android:layout_height="wrap_content"/> </com.hopmet.meijiago.ui.widget.CustomScrollView>
public class CustomScrollView extends ScrollView{ private View mChildView; public CustomScrollView(Context context) { super(context); } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public CustomScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public interface OnScrollStoppedListener{ void onScrollStoppedInTop(); void onScrollStoppedInBottom(); void onScrollStillMove(); } private OnScrollStoppedListener onScrollStoppedListener; @Override protected void onFinishInflate() { super.onFinishInflate(); mChildView = getChildAt(0); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { int bottom = mChildView.getBottom(); bottom -= (getHeight() + getScrollY()); if(bottom == 0){//滚动到底部 if(onScrollStoppedListener != null){ onScrollStoppedListener.onScrollStoppedInBottom(); } }else if(getScrollY() == 0){//滚动到顶部 if(onScrollStoppedListener != null){ onScrollStoppedListener.onScrollStoppedInTop(); } }else{ if(onScrollStoppedListener != null){//在滑动中 onScrollStoppedListener.onScrollStillMove(); } } } public void setOnScrollStoppedListener(CustomScrollView.OnScrollStoppedListener listener){ onScrollStoppedListener = listener; } }
public class ScrollViewContainer extends RelativeLayout { private MyWebView myWebView; /** 自动上滑 */ public static final int AUTO_UP = 0; /** 自动下滑 */ public static final int AUTO_DOWN = 1; /** 动画完成 */ public static final int DONE = 2; /** 动画速度 */ public static final float SPEED = 6.5f; /** 顶部视图 */ private static int TOP_VIEW = 0; /** 底部视图 */ private static int BOTTOM_VIEW = 1; /** 用于计算手滑动的速度 */ private VelocityTracker vt; private int mViewHeight; private int mViewWidth; private CustomScrollView topView; private CustomScrollView bottomView; private int state = DONE; /** 记录当前展示的是哪个view,0是topView,1是bottomView */ private int mCurrentPos = TOP_VIEW; /** 手滑动距离,这个是控制布局的主要变量 */ private int mMarginTop; private MyTimer mTimer; /** 记录最上次的y坐标,在按下和拖动逻辑的最后更新 */ private float mLastY; /** * 用于控制是否变动布局的另一个条件,mCanDrag==0时布局可以拖拽了,mCanDrag==-1时可以舍弃将要到来的第一个move事件, * 这点是去除多点拖动剧变的关键 */ private int mCanDrag; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (mMarginTop != 0) { if (state == AUTO_UP) { // 手指向上滑动时 mMarginTop -= SPEED; if (mMarginTop <= -mViewHeight) { // 越界处理 mMarginTop = -mViewHeight; state = DONE; mCurrentPos = BOTTOM_VIEW; // 1是bottomView } } else if (state == AUTO_DOWN) { // 手指向下滑动时 mMarginTop += SPEED; if (mMarginTop >= 0) { // 越界处理 mMarginTop = 0; state = DONE; mCurrentPos = TOP_VIEW; // 0是topView } } else { mTimer.cancel(); } } else { mTimer.cancel(); } requestLayout(); } }; public ScrollViewContainer(Context context) { super(context); init(); } public ScrollViewContainer(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollViewContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { mTimer = new MyTimer(handler); } /** 手指按下去时刻的y坐标, 每次按下的时刻才会更新 */ private float downY; @Override protected void onFinishInflate() { super.onFinishInflate(); topView = (CustomScrollView) getChildAt(0); bottomView = (CustomScrollView) getChildAt(1); myWebView = (MyWebView) bottomView.findViewById(R.id.web_view_good_detail); vt = VelocityTracker.obtain(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: downY = mLastY = ev.getY(); vt.addMovement(ev); mCanDrag = 0;//布局可以拖拽 break; case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_UP: // 多一只手指按下或抬起时舍弃将要到来的第一个事件move,防止多点拖拽的bug mCanDrag = -1; break; case MotionEvent.ACTION_MOVE: // 如果缺少改逻辑就会导致当滑动到WebView时继续向上滑动webview的内容有会上滑,当下拉时webview的内容不会跟着下滑而是直接切换到上个布局中if (myWebView.getVerticalScrollOffset() != 0) { myWebView.dispatchTouchEvent(ev); return true; } int y = (int) ev.getY(); vt.addMovement(ev); float deltaY = downY - y; // 按下后连续的偏移量 if (mCurrentPos == TOP_VIEW && mCanDrag == 0) { //可上拉,当前展示为topView,可拖拽 if(Math.abs(deltaY) > 150){//防止与横向listView的滑动冲突 mMarginTop += (ev.getY() - mLastY); adjustMarginTop(); if (mMarginTop < -8) { // 防止事件冲突 ev.setAction(MotionEvent.ACTION_CANCEL); } } } else if (mCurrentPos == BOTTOM_VIEW && mCanDrag == 0) {//可下拉,当前展示为bottomView,可拖拽 mMarginTop += (ev.getY() - mLastY); adjustMarginTop(); if (mMarginTop > -mViewHeight) { // 防止事件冲突 ev.setAction(MotionEvent.ACTION_CANCEL); } } else { mCanDrag++; } mLastY = y; // 滑动过程中布局的更新 requestLayout(); break; case MotionEvent.ACTION_UP: vt.addMovement(ev); vt.computeCurrentVelocity(700); // 获取Y方向的速度 float mYV = vt.getYVelocity(); if (mMarginTop == 0 || mMarginTop == -mViewHeight) break; if (Math.abs(mYV) < 500) { // 慢速滑动 速度小于一定值的时候当作静止释放,两个View往哪移动取决于滑动的距离 setState(mMarginTop <= -mViewHeight / 3); } else { // 快速滑动 抬起手指时速度方向决定两个View往哪移动 setState(mYV < 0); } vt.clear(); mTimer.schedule(2); } super.dispatchTouchEvent(ev); // 后续事件(ACTION_MOVE、ACTION_UP)会再传递进来 return true; } /** * * @param up true 向上滑动 false 向下滑动 */ private void setState(boolean up) { if (up) { state = AUTO_UP; } else { state = AUTO_DOWN; } } /** * 调整mMarginTop的值 */ private void adjustMarginTop() { // 防止上下越界 if (mMarginTop > 0) {//上边界为0 mMarginTop = 0; mCurrentPos = TOP_VIEW; } else if (mMarginTop < -mViewHeight) { //下边界为-mViewHeight mMarginTop = -mViewHeight; mCurrentPos = BOTTOM_VIEW; } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // 手指向顶部滑动时,mMarginTop的值负的越来越大,最终达到最小值 topView.layout(0, mMarginTop, mViewWidth, topView.getMeasuredHeight() + mMarginTop); bottomView.layout(0, topView.getMeasuredHeight() + mMarginTop, mViewWidth, topView.getMeasuredHeight() + mMarginTop + bottomView.getMeasuredHeight()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mViewHeight = getMeasuredHeight(); mViewWidth = getMeasuredWidth(); } class MyTimer { private Handler handler; private Timer timer; private MyTask mTask; public MyTimer(Handler handler) { this.handler = handler; timer = new Timer(); } public void schedule(long period) { if (mTask != null) { mTask.cancel(); mTask = null; } mTask = new MyTask(handler); timer.schedule(mTask, 0, period); } public void cancel() { if (mTask != null) { mTask.cancel(); mTask = null; } } class MyTask extends TimerTask { private Handler handler; public MyTask(Handler handler) { this.handler = handler; } @Override public void run() { handler.obtainMessage().sendToTarget(); } } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (vt != null) { vt.recycle(); } } }public class MyWebView extends WebView { public MyWebView(Context context) { this(context, null); } public MyWebView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public int getVerticalScrollOffset() { return this.computeVerticalScrollOffset(); } }继续拖动,查看图文详情是WebView
Ashin相对布局中事件冲突
最新推荐文章于 2018-03-16 09:17:35 发布