【Androidカスタムコントロール】ScrolViewなしで上下2画面スライド


  • 前言
  • 構想
  • コード
  • 使用方法
  • 補足

  • 前言
    最近のプロジェクトの原因には、上下2画面のスライド効果が必要です.viewpagerが左右にスライドして上下にスライドするように想像できます.Srcollviewで実現しようとしたが、ボタンの競合やlistviewレイアウトの競合など、いくつかの理由でカスタムコントロールを自分で書くことにした.
    構想
    以前にSlidingMenuを実現したことがあるので、そのパターンを参考にして、左右スライドを上下スライドにすれば良いと考えています.実は2つの大きさの同じレイアウトで、1つはスクリーンに表示され、もう1つはスクリーンの外に隠され、スライドすると表示されます.
    コード#コード#
    私が引き継いだのはView Groupです.さらにonLayoutメソッドを書き直し、1つのレイアウトが画面を満たし、もう1つは画面の外に配置します.
    public class MySlidingMenu extends ViewGroup {
    
        private static final String TAG = MySlidingMenu.class.getName();
    
        private enum Scroll_State {
            Scroll_to_Open, Scroll_to_Close;
        }
    
        private Scroll_State state;
        private int mMostRecentY;
        private int downY;
        private boolean isOpen = false;
    
        private View menu;
        private View mainView;
    
        private Scroller mScroller;
    
        private OnSlidingMenuListener onSlidingMenuListener;
    
        public MySlidingMenu(Context context, View main, View menu) {
            super(context);
            setMainView(main);
            setMenu(menu);
            init(context);
        }
    
        private void init(Context context) {
            mScroller = new Scroller(context);
        }
    
        @Override
        protected void onLayout(boolean arg0, int l, int t, int r, int b) {
            mainView.layout(l,t,r,b);
            //         
            menu.layout(l, menu.getMeasuredHeight(), r, menu.getMeasuredHeight()*2);
        }
    
        public void setMainView(View view) {
            mainView = view;
            addView(mainView);
        }
    
        public void setMenu(View view) {
            menu = view;
            addView(menu);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            mainView.measure(widthMeasureSpec, heightMeasureSpec);
            menu.measure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mMostRecentY = (int) event.getY();
                    downY = (int) event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int moveY = (int) event.getY();
                    int deltaY = mMostRecentY - moveY;
                    //                           Scroll  
                    if ((!isOpen && (downY - moveY) > 0)
                            || (isOpen && (downY - moveY) < 0)) {
                        scrollBy(0, deltaY);
                    }
                    mMostRecentY = moveY;
                    break;
                case MotionEvent.ACTION_UP:
                    int upY = (int) event.getY();
                    int dy = upY - downY;
                    if (!isOpen) {
                    //            menu            
                        if (dy < - menu.getMeasuredHeight() / 3) {
                            state = Scroll_State.Scroll_to_Open;
                        } else {
                            state = Scroll_State.Scroll_to_Close;
                        }
                    } else {
                    //               menu   ,        menu     ,    
                        if (dy > menu.getMeasuredHeight() / 3) {
                            state = Scroll_State.Scroll_to_Close;
                        } else {
                            state = Scroll_State.Scroll_to_Open;
                        }
                    }
                    smoothScrollto();
                    break;
                default:
                    break;
            }
            return true;
        }
    
        private void smoothScrollto() {
            int scrolly = getScrollY();
            switch (state) {
                case Scroll_to_Close:
                    mScroller.startScroll(0, scrolly, 0, - scrolly, 500);
                    isOpen = false;
                    break;
                case Scroll_to_Open:
                    mScroller.startScroll(0, scrolly, 0, menu.getMeasuredHeight()- scrolly, 500);
                    isOpen = true;
                    break;
                default:
                    break;
            }
            //        invalidate,           computeScroll  
            invalidate();
        }
    
        @Override
        public void computeScroll() {
            //    computeScrollOffset     false,   startScroll       true
            if (mScroller.computeScrollOffset()) {
                scrollTo(0, mScroller.getCurrY());
                postInvalidate();
            }
            super.computeScroll();
        }
    }

    セグメント全体のコードは複雑ではなく、最も主要なのは座標の計算です.startScroll(int startX,int startY,int dx,int dy,int duration)という方法は座標スライドに対して計算しにくい.最初はdx、dyが正しい方向、負の方向に迷っていました.後でソースコードを見ました.
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {  
        mMode = SCROLL_MODE;  
        mFinished = false;  
        mDuration = duration;  
        mStartTime = AnimationUtils.currentAnimationTimeMillis();  
        mStartX = startX;  
        mStartY = startY;  //      
        mFinalX = startX + dx;  
        mFinalY = startY + dy;  //      
        mDeltaX = dx;  
        mDeltaY = dy;  
        mDurationReciprocal = 1.0f / (float) mDuration;  
        // This controls the viscous fluid effect (how much of it)  
        mViscousFluidScale = 8.0f;  
        // must be set to 1.0 (used in viscousFluid())  
        mViscousFluidNormalize = 1.0f;  
        mViscousFluidNormalize = 1.0f / viscousFluid(1.0f);  
    }  

    したがって、A(a 1,a 2)からB(b 1,b 2)にスライドすると、startScroll(a 1,a 2,b 1−a 1,b 2−a 2)だけでよい.主に差を求める.
    使用方法
        private MySlidingMenu mSlidingMenu;
        ......
        mSlidingMenu = new MySlidingMenu(this, LayoutInflater
             .from(this).inflate(R.layout.fragment1, null), LayoutInflater
             .from(this).inflate(R.layout.fragment2, null));
    

    補足
    これはlistviewのスライド競合を引き起こし、列数が固定されている場合、listviewを直接書き換えるonTouchEventがfalseを返すように画面に表示されていればよい.比較的多い場合はonTouchEvent()でgetParent()を通過する.requestDisallowInterceptTouchEvent(bool)は、イベントに応答するコントロールを設定します.
    一般的な考え方はlistが頭と尾をスライドするときに、対応する方向のスライドイベントを親コンポーネントに伝えることです.