zoukankan      html  css  js  c++  java
  • View的滑动冲突

    一、常见的滑动冲突

    场景1:外部滑动和内部滑动不一致

    场景2:外部滑动和内部滑动一致

    场景3:上面两种情况的嵌套

    二、滑动冲突的处理方法

    场景一:根据水平滑动还是竖直滑动判断到底由谁来拦截事件。

    场景二:从业务上找突破点,比如内部为ListView,点在ListView内部的时候让ListView滑动,如果在ListView外则让父View滑动。

    场景三:同样还是从业务上寻找突破点。

    三、各种拦截的方法(暂时只介绍外部拦截法,内部拦截发P158)

    原理:点击事件都经过父控件的拦截处理,如果父控件需要则拦截事件,不需要则不拦截。

    四、示例

    场景一:仿ViewPager,有三个页面,每个页面有一个ListView

    使用:①、创建ScrollerLinearLayout继承LinearLayout 并在activity_main.xml中使用②、在MainActivity中动态加载三个ListView ③、重写ScrollerLinearLayout的onIntereptTouchEvent()方法

    重写的逻辑:当DOWN的时候,滑动动画还在进行的时候,拦截、当MOVE的时候,判断坐标是横移的距离还是竖移距离大,如果横移大拦截,竖移大不拦截。

    ④、重写onTouchEvent():如果DOWN,则不处理。MOVE,根据滑动的方向和距离,根据scrollTo/scrollBy进行滑动。UP,则获取滑动的速度,根据速度判断如果速度快的话,则滑动到下一页或前一页。如果慢的话,判断如果当前位移滑动过半页了则滑动到下一页或前一页。

    ⑤、自动滑动的方法 详见View的滑动

    步骤一:初始化相关信息

    <!--已创建ScrollerLinearLayout-->
    <com.maikefengchao.scrollercomplict.ScrollerLinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:id="@+id/main_linear_scroller"
        tools:context="com.maikefengchao.scrollercomplict.MainActivity">
    
    </com.maikefengchao.scrollercomplict.ScrollerLinearLayout>
    activity_main
    <!--需要动态载入的页面的layout-->
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/content_tv_title"
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:background="@android:color/holo_blue_dark"/>
        <ListView
            android:id="@+id/content_lv_list"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"></ListView>
    </LinearLayout>
    content_layout

    步骤二:动态加载content_layout布局,内含ListView

    public class MainActivity extends AppCompatActivity {
    
        private ScrollerLinearLayout mScrollearLinear;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
        }
    
        private void initView(){
            //获取Horizon对象
            mScrollearLinear = (ScrollerLinearLayout)findViewById(R.id.main_linear_scroller);
            //获取当前屏幕的宽高,为将content_layout的宽高与屏幕一直,如果是match_parent,三个布局的间距无限长。
            DisplayMetrics metric = new DisplayMetrics();
            getWindow().getWindowManager().getDefaultDisplay().getMetrics(metric);
            int screenWidth = metric.widthPixels;
            int screenHeight = metric.heightPixels;
            
            //将content_layout装入horizon中 :动态加载layout
            for(int i=0; i<3; ++i){
                //加载布局获取ViewGroup
                ViewGroup layout = (ViewGroup) getLayoutInflater().inflate(R.layout.content_layout,mScrollearLinear,false);
                //设置layout的宽高
                layout.getLayoutParams().width = screenWidth;
                layout.getLayoutParams().height = screenHeight;
                //动态配置View的子View
                TextView textView = (TextView)layout.findViewById(R.id.content_tv_title);
                textView.setText("Page:"+i);
                //设置View的背景颜色
                layout.setBackgroundColor(Color.rgb(255/(i+1),255/(i+1),0));
                //创建ListView
                createList(layout);
                //真正添加进View中
                mScrollearLinear.addView(layout);
            }
        }
        //普通ListView的创建不解释
        private void createList(ViewGroup viewGroup){
            ListView listView = (ListView) viewGroup.findViewById(R.id.content_lv_list);
            ArrayList<String> arrayList = new ArrayList<>();
            for(int i=0; i<30; ++i){
                arrayList.add("name#"+i);
            }
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.listview_content_item,R.id.item_tv_content,arrayList);
            listView.setAdapter(adapter);
        }
    }
    MainActivity

    步骤三:重写ScrollerLinearLayout的onIntereptTouchEvent()拦截事件

    private static final String TAG = "ScrollerLinearLayout";
        //滑动类,用来滑动切换界面的
        private Scroller mScroller;
        private Context mContext;
        //速度类,用来判断手指滑动的速度
        private VelocityTracker mVelocityTracker;
        //onInterceptTouchEvent中,判断手指滑动的距离,选择是否拦截
        private int mOldInterceptX = 0;
        private int mOldInterceptY = 0;
    
        //onTouchEvent中,新旧点判断移动距离,进行滑动
        private int mOldX = 0;
        private int mOldY = 0;
        //当前子类的序号
        private int mChildIndex = 0;
        //有多少个子类
        private int mChildSize;
        //当前子类的宽度
        private int mChildWidth;
    
        public ScrollerLinearLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public ScrollerLinearLayout(Context context) {
            super(context);
            initView(context);
        }
    
        public ScrollerLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        //初始化对象
        private void initView(Context context){
            mContext = context;
            mScroller = new Scroller(context);
            mVelocityTracker = VelocityTracker.obtain();
        }
        //获取子View的宽/高/数量信息,有四种方法获取View的宽、高、数量①
        @Override
        public void onWindowFocusChanged(boolean hasWindowFocus) {
            super.onWindowFocusChanged(hasWindowFocus);
            mChildWidth = getChildAt(0).getWidth();
            mChildSize = getChildCount();
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            boolean intercepted = false;
            int currentX = (int)ev.getX();
            int currentY = (int)ev.getY();
            switch (ev.getAction()){
                /*当子View正在滑动的时候拦截*/
                case MotionEvent.ACTION_DOWN:
                    if (!mScroller.isFinished()){
                        //滑动优化,可以不添加
                        mScroller.abortAnimation();
                        intercepted = true;
                    }
                    break;
                /*获取当前移动举例信息,判断用户是竖划还是横划*/
                case MotionEvent.ACTION_MOVE:
                    int deltaX = mOldInterceptX - currentX;
                    int deltaY = mOldInterceptY - currentY;
                    if (Math.abs(deltaX) > Math.abs(deltaY)){
                        intercepted = true;
                    }
                    else {
                        intercepted = false;
                    }
                    break;
                /*不拦截*/
                case MotionEvent.ACTION_UP:
                    intercepted = false;
                    break;
            }
            mOldInterceptX = currentX;
            mOldInterceptY = currentY;
            return intercepted;
        }
    ScrollerLinearLayout

    步骤四:重写onTouchEvent()方法,并配置自动滑动的类

     //....接上    
    
    @Override
        public boolean onTouchEvent(MotionEvent event) {
            /*速度类需要获取事件对象,才能使用*/
            mVelocityTracker.addMovement(event);
            switch (event.getAction()){
                case MotionEvent.ACTION_DOWN:
                    break;
                /*获取滑动的距离,并用方法滑动*/
                case MotionEvent.ACTION_MOVE:
                    int destX = (int)(mOldX - event.getX());
                    int destY = 0;
                    /*注:为正向左边移动,负向右边移动*/
                    scrollBy(destX, destY);
                    break;
                /*自动滑动,当速度大于50时候或当滑动距离大于一半的时候自动滑动到下一View或者前一View*/
                case MotionEvent.ACTION_UP:
                    int scrollX = getScrollX();
                    //VelocityTracker的使用见其他文章
                    mVelocityTracker.computeCurrentVelocity(1000);
                    float xVelocity = mVelocityTracker.getXVelocity();
                    if (Math.abs(xVelocity) >= 50 ){
                        /*从左向右滑动为正,从右向左为负*/
                            mChildIndex = xVelocity>0?mChildIndex-1:mChildIndex+1;
                    }
                    else {
                        //滑动超过屏幕的1/2的时候
                        mChildIndex = (scrollX + (mChildWidth*1)/2)/mChildWidth;
                    }
                    //限制只能滑动有的页面
                    mChildIndex = Math.max(0,Math.min(mChildIndex,mChildSize-1));
                    int dx = mChildWidth*mChildIndex - scrollX;
                    //初始化滑动类的滑动信息,详见View的滑动这一章
                    smoothScrollBy(dx);
                    mVelocityTracker.clear();
                    break;
            }
            mOldX = (int)event.getX();
            mOldY = (int)event.getY();
            return true;
        }
    
        private void smoothScrollBy(int dx){
            mScroller.startScroll(getScrollX(),0,dx,0,1000);
            invalidate();
        }
    
        @Override
        public void computeScroll() {
            super.computeScroll();
            if (mScroller.computeScrollOffset()){
                scrollTo(mScroller.getCurrX(), 0);
                postInvalidate();
            }
        }
    ScrollerLinearLayout


    解释①:因为这是第四章的内容,到第四章会详细解释

    为什么在构造方法中,无法调用getChildAt()和getChildCount()方法。因为在构造的过程中并没有调用measure()方法,无法获取子View的任何信息。

    所以同样在Activity中也一样,如果View没有完全创建完成,则Activity在任何位置都获取不到view的宽/高。

    (暂时使用两种P190页详解)所以最适合调用的四种方法(第一种方法可以在View中,其他方法适用于在Activity中获取需要的View的宽高):

    ①、onWindowFocusChanged:该方法在View初始化完成之后才会被调用,当Activity的窗口得到焦点和失去焦点的时候都会调用一次。

     
       @Override
        public void onWindowFocusChanged(boolean hasWindowFocus) {
            super.onWindowFocusChanged(hasWindowFocus);
            if (hasFocus()){
                int width = getMeasuredWidth();
                int height = getMeasuredHeight();
            }
        }
    SimpleView

    ②、view.post(runnable):将runnable投递到消息队列的队尾。

  • 相关阅读:
    自定义序列化类注意事项
    HashMap中的modCount
    transient
    30张图带你彻底理解红黑树(转)
    hashcode和hash算法
    线程池
    VS2015 NuGet错误:远程证书无效
    解决前台JS弹框Alert点击确定页面会刷新
    Regex中Replace方法的简单实用
    .ashx 一般处理程序
  • 原文地址:https://www.cnblogs.com/rookiechen/p/5408832.html
Copyright © 2011-2022 走看看