【问题标题】:Android: four directional navigationAndroid:四向导航
【发布时间】:2013-10-10 15:32:39
【问题描述】:

我一直在尝试实现四向滑动导航。我有一些关于如何把东西放在一起的问题。

  1. 考虑使用片段。会锻炼吗?
  2. 另一个想法是创建一个大的相对布局并如图所示排列视图。但在旋转设备纵向/横向时可能会出现问题。
  3. 以某种方式模拟滑动效果

如何锻炼所有这些点?任何人都可以帮助它。 我已经找到了有关TouchMove 功能的问题

  1. Touch movement in only four directions?
  2. Fling gesture detection on grid layout
  3. https://github.com/JakeWharton/Android-DirectionalViewPager/

感谢任何建议和帮助,原因是我对 android 开发比较陌生。

【问题讨论】:

  • Another idea is to create a big relative layout and arrange views as shown on the picture. But possibly will have issues when rotating device portait/landscape. 如果你也实现了横向模式的布局,你应该没有问题。

标签: java android user-interface


【解决方案1】:

不是那么难的任务,多亏了 Scroller,它几乎是简单的蛋糕

如果您想放置一些交互式视图,例如 Button 等(我敢打赌),您需要覆盖 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);
        }
    }
}

要对其进行测试,请将其添加到您的 Activity.onCreate:

ViewGroup layout = new FourDirectionLayout(this);
setContentView(layout);

【讨论】:

  • 整洁。我建议使用 Fragments 的原因是因为我认为视图会很重,并且我讨厌拥有甚至不可见的重视图树。 (在您的解决方案中,它们都将被 画出屏幕)。但是,是的,我过于复杂了,主要是因为我考虑的是动画而不是经典滚动(我以前处理过嵌套滚动......)。
  • @sultan 更新为优化实现,将课程减少了约 30 行 cide
【解决方案2】:

这不是一件容易的事,恐怕没有“做这个和不做那个”的答案。

让我们把简单的部分排除在外 - 您检测滑动的方式是使用 GestureDetector。您将收到onFling(或onScroll)事件,然后您可以更新Scroller 以获取实际坐标。存储/显示屏幕的方式更加困难。

在我的脑海中,RelativeLayout 有点难以制作动画。滚动会很痛苦,尽管通过改变中间的屏幕,让它布局并使用ViewTreeObserver.onPreDraw 动画到新位置(在它们第一次绘制之前),可以更容易地制作动画。我怀疑滚动会有点困难(不过,如果你扩展 RelativeLayout,你可以改变孩子的位置,而不需要重新布局或重新测量,所以也许这不是一个坏主意)。

就我个人而言,我会扩展FrameLayout,在孩子们做出反应之前检测出投掷(参见onInterceptTouchEvent),然后将正确的片段放在那里,并带有适当的过渡动画。似乎是最巧妙的思考方式。

附:我希望您可以只使用 DirectionalViewPager 并在运行时更改方向,但问题是中间屏幕 - 它必须响应两个方向。也许从那个类扩展不是一个坏主意......

【讨论】:

  • 谢谢@Delyan。我会用FrameLayout 试试看,结果一出来,就会发布屏幕截图。
  • @Delyan 我认为您的解决方案过于复杂:请参阅我的答案,现在经过一些优化,我的课程只需要 99 行代码:)
【解决方案3】:

我能想到的最接近的是谷歌地图以及它们让用户在地图中导航(滚动、翻动和擦除)的方式。

当您使用慢速网络滚动时,您在那里看到的是,它们似乎适用于 4x5 网格。因此,放置在网格上的 20 个单张图像。并且在屏幕外的每个方向上可能还有一个。

你现在需要做两件事:

  • 获取触摸事件以检测用户希望在哪个位置看到的内容。

  • 将您的内容放在网格上。您可以将网格构建为 FrameLayout 上的 ImageView 或 SurfaceView 上的 Bitmap。使用后者,您可以更好地控制绘图和放置。使用第一个放置文本,按钮等更容易。根据您想要展示的内容,您可以使用其中一种方法。

【讨论】:

    猜你喜欢
    • 2012-12-10
    • 1970-01-01
    • 1970-01-01
    • 2018-11-09
    • 1970-01-01
    • 2014-10-02
    • 2011-10-24
    • 1970-01-01
    相关资源
    最近更新 更多