Android: four directional navigation

后端 未结 3 543
野趣味
野趣味 2020-12-17 06:44

I\'ve been trying to implement four directional swipe navigation. And I have some questions on how to put things together.

  1. Thinking about using fragments. Will
相关标签:
3条回答
  • 2020-12-17 07:22

    not so hard task, thanks to Scroller its almost easy cake

    if you want to put some interactive Views like Button etc (and i bet you will) you need to override onInterceptTouchEvent(MotionEvent ev)

    public class FourDirectionLayout extends ViewGroup {
        private GestureDetector mDetector;
        private Scroller mScroller;
        private final String[] TEXTS = {
                "left view, left swipe only",
                "right view, right swipe only",
                "top view, top swipe only",
                "bottom view, bottom swipe only",
                "central view, swipe to the left, right, top or bottom",
        };
        private final int[] COLORS = {
                0xaa0000ff, 0xaa0000ff, 0xaaff0000, 0xaaff0000, 0xaa00ff00
        };
        private final int[] PACKED_OFFSETS = {
                -1, 0, 1, 0, 0, -1, 0, 1, 0, 0
        };
    
        public FourDirectionLayout(Context context) {
            super(context);
            for (int i = 0; i < TEXTS.length; i++) {
                TextView tv = new TextView(context);
                tv.setTag(i);
                tv.setTextSize(32);
                tv.setTypeface(Typeface.DEFAULT_BOLD);
                tv.setTextColor(0xffeeeeee);
                tv.setText(TEXTS[i]);
                tv.setBackgroundColor(COLORS[i]);
                addView(tv);
            }
            mDetector = new GestureDetector(context, mListener);
            mScroller = new Scroller(context);
        }
    
        private OnGestureListener mListener = new SimpleOnGestureListener() {
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (!mScroller.isFinished()) {
                    return false;
                }
                int sx = getScrollX();
                int sy = getScrollY();
                int w = getWidth();
                int h = getHeight();
                int DURATION = 500;
                // check if horizontal/vertical fling
                if (Math.abs(velocityX) > Math.abs(velocityY)) {
                    if (sy != 0 || velocityX * sx < 0) {
                        return false;
                    }
    //                DURATION = (int) (1000 * w / Math.abs(velocityX));
                    int distance = velocityX < 0? w : -w;
                    mScroller.startScroll(sx, sy, distance, 0, DURATION);
                } else {
                    if (sx != 0 || velocityY * sy < 0) {
                        return false;
                    }
    //                DURATION = (int) (1000 * h / Math.abs(velocityY));
                    int distance = velocityY < 0? h : -h;
                    mScroller.startScroll(sx, sy, 0, distance, DURATION);
                }
                invalidate();
                return true;
            }
        };
    
        @Override
        public void computeScroll() {
            if (mScroller.computeScrollOffset()) {
                scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
                invalidate();
            }
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            mDetector.onTouchEvent(event);
            return true;
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int cnt = getChildCount();
            for (int i = 0; i < cnt ; i++) {
                View child = getChildAt(i);
                int idx = (Integer) child.getTag() << 1;
                int xOffset = (r - l) * PACKED_OFFSETS[idx];
                int yOffset = (b - t) * PACKED_OFFSETS[idx + 1];
                child.layout(l + xOffset, t + yOffset, r + xOffset, b + yOffset);
            }
        }
    }
    

    to test it add this in your Activity.onCreate:

    ViewGroup layout = new FourDirectionLayout(this);
    setContentView(layout);
    
    0 讨论(0)
  • 2020-12-17 07:35

    The closest I can think of, is Google Maps and the way they let the user navigate (scroll, fling and wipe) thru the map.

    What you see there, when scrolling around with a slow network, is that they seem to work with a 4x5 grid. So 20 single images that are placed on a grid. And probably one more in each direction off screen.

    You now need to do two things:

    • get touch events to detect, what the user wants to see at which position.

    • place your content on the grid. You can either build the grid as ImageView's on a FrameLayout or as Bitmap's on a SurfaceView. With the latter you have more control over drawing and placement. With the first putting text, buttons and such is easier. Depending on what you want to show you can go with one these approaches.

    0 讨论(0)
  • 2020-12-17 07:36

    Not an easy task and I'm afraid there isn't a do-this-and-don't-do-that answer.

    Let's get the easy part out of the way - the way you detect swipes is using GestureDetector. You'll get an onFling (or onScroll) event and then you can update a Scroller to get actual coordinates. The way you store/display the screens is more difficult.

    Off the top of my head, RelativeLayout would be kinda hard to animate. Scrolling would be a pain to do, though flings can be easier to animate by changing the screen in the middle, letting it lay out and using ViewTreeObserver.onPreDraw to animate to the new positions (before they are first drawn). Scrolling would be a bit harder, I suspect (though, if you extend RelativeLayout, you can just change the children's position, without relayout or remeasure, so maybe it's not such a bad idea).

    Personally, I would extend FrameLayout, detect flings before the children can react (see onInterceptTouchEvent) and put the right fragment in there, with the appropriate transition animations. Seems to be the neatest way to think about it.

    P.S. I was hoping you could just use DirectionalViewPager and change the direction at run time but the problem is the middle screen - it has to respond to both directions. Maybe extending from that class isn't a bad idea...

    0 讨论(0)
提交回复
热议问题