android 可上下滑动切换区域 左右滑动

目前我们项目有个需求,商品详情页需要仿淘宝 而且下面的页卡可以滑动切换,于是用ViewGroup自定义了一个上下方向的ViewPager,并且需要处理各种事件传递,相信通过自定义ViewGroup我们可以实现很多不一样的页面结构需求,

github地址https://github.com/ouyangfeng/CustomerViewGroup.git

转载请注明:http://blog.csdn.net/ertingkele/article/details/49926187


以下是自定义的跟布局View源码

package com.liushengfan.test.customerviewgroup.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.Scroller;

import com.liushengfan.test.customerviewgroup.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @author liushengfan
 * @see android.support.v4.view.ViewPager
 *         <p/>
 *         垂直方向 ViewPager
 */
public class ViewGroupScrollView extends ViewGroup implements ViewPager.OnPageChangeListener {

    private Context mContext;

    private View mObservedView;//正在监听的 View

    private ViewPager mViewPager;//底部ViewPager

    private View mUpView;//第一部分View
    private View mDownView;//第二部分View


    private boolean viewChang = false;//第一部分与第二部分 是否已经切换  相对于初始位置

    private Scroller myScroller;

    private boolean isFling = false;

    private int boundaryY = 0;

    MyGestureListener gestureListener;

    public ViewGroupScrollView(Context context) {
        super(context);
    }

    public ViewGroupScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        init();
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ViewGroupScrollView, 0, 0);
        int downViewLayoutId = a.getResourceId(R.styleable.ViewGroupScrollView_downView, 0);
        int upViewLayoutId = a.getResourceId(R.styleable.ViewGroupScrollView_upView, 0);
        mUpView = LayoutInflater.from(context).inflate(upViewLayoutId, null);
        mDownView = LayoutInflater.from(context).inflate(downViewLayoutId, null);
        a.recycle();
        if (null == mUpView || null == mDownView) {
            throw new RuntimeException("upView or downView can not be null");
        }
        addView(mUpView);
        addView(mDownView);
    }

    private void init() {
        myScroller = new Scroller(mContext);
        gestureListener = new MyGestureListener();
        detector = new GestureDetector(mContext, gestureListener);

    }

    /**
     * 设置下部分View的资源ID
     *
     * @param layoutId
     */
    public void setViewPagerLayoutId(int layoutId) {
        View v = mDownView.findViewById(layoutId);
        if (null == v || !(v instanceof ViewPager)) {
            throw new RuntimeException("can not find ViewPager in the second part of ViewGroupScrollView");
        }
        mViewPager = (ViewPager) v;
        //ViewPager 设置监听器
        mViewPager.addOnPageChangeListener(this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        MeasureSpec.getSize(widthMeasureSpec);
        MeasureSpec.getMode(widthMeasureSpec);

        for (int i = 0; i < getChildCount(); i++) {
            View view = getChildAt(i);
            view.measure(widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        View viewUp = getChildAt(0);
        View viewDown = getChildAt(1);
        boundaryY = viewUp.getMeasuredHeight();
        viewUp.layout(l, t, r, boundaryY);
        viewDown.layout(l, boundaryY, r, b + boundaryY);
        if (changed) {
            moveToDest();
        }
    }

    private GestureDetector detector;

    public int curIndex;// 0 top 1 bottom

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        try {
            return super.dispatchTouchEvent(ev);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        detector.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                if (!isFling) {
                    moveToDest();
                }
                isFling = false;
                break;

            default:
                break;
        }
        return true;
    }

    /**
     * 两部分View自动切换
     */
    private void moveToDest() {
        int destId = (getScrollY() / (boundaryY / 2)) >= 1 ? 1 : 0;
        moveToDest(destId);
    }

    /**
     * 两部分切换
     *
     * @param destId
     */
    public void moveToDest(int destId) {
        if (0 == destId || 1 == destId) {
            int distance = destId * boundaryY - getScrollY();
            curIndex = destId;
            myScroller.startScroll(getScrollX(), getScrollY(), 0, distance, Math.abs(distance / 2));
            invalidate();
        }
    }

    /**
     * 计算滚动位置
     */
    @Override
    public void computeScroll() {
        if (myScroller.computeScrollOffset()) {
            int curY = myScroller.getCurrY();
            scrollTo(0, curY);
            invalidate();
            if (0 == curY) {
                viewChang = false;
                mObservedView = mUpView;
            } else if (boundaryY == curY) {
                viewChang = true;
                mObservedView = getCurChild_vp(mViewPager);
            }
        }
    }

    private int startY = 0;
    private int startX = 0;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercept = false;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startY = (int) ev.getY();
                startX = (int) ev.getX();
                break;
            case MotionEvent.ACTION_UP:
                startX = 0;
                startY = 0;
                intercept = false;
                break;
            case MotionEvent.ACTION_MOVE:
                int currentY = (int) ev.getY();
                int currentX = (int) ev.getX();
                int distanceY = currentY - startY;
                int distanceX = currentX - startX;
                if (Math.abs(distanceY) - Math.abs(distanceX) > 5) {
                    boolean interceptFromChildFeedback = isParentViewShouldInterceptEnvent(viewChang, mObservedView);
                    if (viewChang) {
                        if (interceptFromChildFeedback && distanceY > 0) {
                            intercept = true;
                        } else {
                            intercept = false;
                        }
                    } else {
                        if (interceptFromChildFeedback && distanceY < 0) {
                            intercept = true;
                        } else {
                            intercept = false;
                        }
                    }
                }
                startY = currentY;
                startX = currentX;
                break;
            default:
                break;
        }
        gestureListener.setInterrupt(intercept);
        detector.onTouchEvent(ev);
        return intercept;
    }

    private class MyGestureListener extends GestureDetector.SimpleOnGestureListener {
        private boolean interrupt = false;

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (interrupt) {
                if (0 == getScrollY() && distanceY > 0) {
                    scrollBy(0, (int) distanceY);
                } else if (getScrollY() > 0 && getScrollY() < (boundaryY)) {
                    scrollBy(0, (int) distanceY);
                } else if ((boundaryY) == getScrollY() && distanceY < 0) {
                    scrollBy(0, (int) distanceY);
                }
            }
            // scrollBy(0, (int) distanceY);
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            isFling = true;
            if (velocityY > 0) {
                moveToDest(0);
            } else {
                moveToDest(1);
            }
            return true;
        }

        public void setInterrupt(boolean interrupt) {
            this.interrupt = interrupt;
        }
    }

    public boolean isViewChang() {
        return viewChang;
    }

    public void setViewChang(boolean viewChang) {
        this.viewChang = viewChang;
    }

    /**
     * 是否应该中断事件传输
     *
     * @param viewChang    是否上下两部分布局已经切换
     * @param observedView 被观察的可滚动的View
     * @return
     */
    private boolean isParentViewShouldInterceptEnvent(boolean viewChang, View observedView) {
        boolean intercept = true;
        if (null == observedView) {
            return intercept;
        }
        if (viewChang) {
            if (observedView instanceof ListView) {
                ListView listView = (ListView) observedView;
                if (0 != listView.getChildCount()) {
                    if (0 == listView.getFirstVisiblePosition() && 0 == listView.getChildAt(0).getTop()) {
                        intercept = true;
                    } else {
                        intercept = false;
                    }
                } else {
                    intercept = true;
                }
            } else if (observedView instanceof ScrollView) {
                ScrollView scrollView = (ScrollView) observedView;
                if (scrollView.getScrollY() == 0) {
                    intercept = true;
                } else {
                    intercept = false;
                }
            } else {
                intercept = true;
            }
        } else {
            if (observedView instanceof ListView) {
                //TODO 支持ListView 判断滑动到底部
            } else if (observedView instanceof ScrollView) {
                ScrollView scrollView = (ScrollView) observedView;
                View contentView = scrollView.getChildAt(0);
                if (contentView != null && contentView.getMeasuredHeight() <= scrollView.getScrollY() + scrollView.getHeight()) {
                    intercept = true;
                } else {
                    intercept = false;
                }
            } else {
                intercept = true;
            }
        }
        return intercept;
    }


    //ViewPager Listener begin
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        if (isViewChang()) {
            mObservedView = getCurChild_vp(mViewPager);
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }
    //ViewPager Listener end

    /**
     * 获取ViewPager当前显示的View
     *
     * @param vp
     * @return
     */
    public static View getCurChild_vp(ViewPager vp) {

        int childCnt = vp.getChildCount();
        int totalCnt = vp.getAdapter().getCount();
        int curItem = vp.getCurrentItem();

        int targetIndex = 0;

        // 若"已加载child未达到应有值",则在边界 、或总数达不到limit
        if (childCnt < vp.getOffscreenPageLimit() * 2 + 1) {
            // 若-项数不足-加载所有至limit,直接返回当前
            if (childCnt == totalCnt)
                targetIndex = curItem;
            else
            // 若足
            {
                // 若在左边界(即左边child数未达到limit)
                if (curItem - vp.getOffscreenPageLimit() < 0)
                    targetIndex = curItem;
                    // 右边界
                else
                    targetIndex = vp.getOffscreenPageLimit();
            }
        }
        // childCnt完整(即总项>childCnt,且不在边界)
        else
            targetIndex = vp.getOffscreenPageLimit();

        // 取-子元素
        List<View> vs = new ArrayList<View>();
        for (int i = 0; i < childCnt; i++)
            vs.add(vp.getChildAt(i));

        // 对子元素-排序,因默认排序-不一定正确(viewpager内部机制)
        Collections.sort(vs, new Comparator<View>() {
            @Override
            public int compare(View lhs, View rhs) {
                // TODO Auto-generated method stub
                if (lhs.getLeft() > rhs.getLeft())
                    return 1;
                else if (lhs.getLeft() < rhs.getLeft())
                    return -1;
                else
                    return 0;
            }
        });

        // debug
        // for (int i = 0; i<childCnt; i++)
        // System.out.println("nimei>>vp-"+i+".x:"+vs.get(i).getLeft());
        // System.out.println("nimei>>index:"+targetIndex);

        return vs.get(targetIndex);
    }

}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在Android设备上,实现上下滑切换视频可以通过以下步骤完成: 1. 首先,你需要创建一个包含视频播放的页面或者活动。可以使用VideoView或ExoPlayer等多媒体库来实现视频播放功能。 2. 在该页面或者活动中,你需要设置一个手势监听器,以便捕捉用户的上下滑动手势。可以使用GestureDetector来实现手势监听功能。 3. 在手势监听器中,你可以根据用户的上下滑动方向来切换视频。比如,当用户向上滑动时,你可以切换到下一个视频,在你的视频播放列表中,你可以用一个变量来记录当前正在播放的视频索引,然后在手势监听器中处理上下滑动的逻辑,切换到相应的视频。 4. 另外,你还可以在切换视频的同时更新相关的视频信息,比如视频标题、作者等。这可以通过更改相关控件的内容来实现。 5. 最后,为了提升用户体验,你可以添加一些动画效果,比如淡入淡出动画或滑动动画,在用户切换视频时使用,使切换过程更加平滑和自然。 总之,通过设置手势监听器并根据用户上下滑动的意图来切换视频,我们可以在Android设备上实现上下滑切换视频的功能。同时,你也可以根据具体需求自定义其他的切换方式和效果。 ### 回答2: 在Android上实现上下滑切换视频的功能可以通过以下步骤实现。 首先,需要一个用于展示视频的视图组件,可以选择使用VideoView或ExoPlayer等。 然后,在布局文件中嵌入该视图组件,并为其设置一个唯一的ID。 接下来,在相关的Java或Kotlin代码中,通过findViewById方法获取到该视图组件的实例。 在获取到实例之后,需要为该组件添加一个触摸监听器,以便监听用户的上下滑动手势。 在触摸监听器的onTouch方法中,可以通过获取MotionEvent对象来判断用户的手势动作。如果用户进行了向上滑动的动作,则切换到下一个视频;如果用户进行了向下滑动的动作,则切换到上一个视频。 要实现视频切换的功能,可以使用一个视频列表来存储所有视频的URL或文件路径。在切换视频时,通过修改VideoView或ExoPlayer组件的视频源来切换到下一个或上一个视频。 另外,为了确保用户在滑动过程中可以流畅地看到视频内容,可以考虑使用预加载的方式加载前后的视频。 最后,测试应用程序,确保上下滑动手势可以正确切换视频,并且视频播放的过程中没有明显的卡顿或延迟现象。 以上是一种实现上下滑切换视频的方式,具体的实现方式还需要根据具体的应用场景和需求进行调整和优化。 ### 回答3: 在Android上,可以使用RecyclerViewViewPager来实现上下滑切换视频的功能。 首先,我们需要创建一个RecyclerView,并设置其布局管理器为垂直方向。接下来,我们需要自定义一个ViewHolder和一个适配器,用于加载视频和绑定到RecyclerView上。 在RecyclerView的适配器中,我们可以使用ViewPager作为每个子项的容器,用于展示视频。在ViewPager的适配器中,我们可以根据视频数量创建相应数量的Fragment,并在每个Fragment中展示对应的视频。 在实现切换的过程中,我们可以使用GestureDetector来监听用户的上下滑动手势。当用户向上滑动时,我们可以通过ViewPager的setCurrentItem方法将当前的视频切换到下一个视频。当用户向下滑动时,我们可以将当前的视频切换到上一个视频。 另外,我们还可以通过监听ViewPager的滑动事件,来实时更新当前视频的进度和展示相关的控制按钮,如播放/暂停、全屏等。 最后,我们还可以在RecyclerView的适配器中实现点击事件,当用户点击某个视频时,我们可以通过ViewPager的setCurrentItem方法将当前的视频切换到被点击的视频,并进行相应的处理。 综上所述,通过使用RecyclerViewViewPager,以及监听用户手势和ViewPager的滑动事件,我们可以在Android上实现上下滑切换视频的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值