【问题标题】:which would be the best to do make a horizontal scrolling app in android?在android中制作水平滚动应用程序是最好的选择?
【发布时间】:2011-09-02 02:01:01
【问题描述】:

我必须制作一个用户在屏幕上移动的应用程序,就像我们的 android 主屏幕一样。

有一个图像列表,我们可以在水平滚动器中滚动图像。

用户不能改变图片的位置,它就像几个缩略图排列在一个水平的屏幕上

这就像 iPhone 应用程序开发中的分页控件。

我试图找到方法来做到这一点,但我对 android 还很陌生,我想知道实现上述目标的最佳方法?

我听说过画廊控制,但我不确定它是否适合我的目的。

此外,如果您可以提供指向您建议的答案的链接。如果涉及到一个新的控制器,那就太好了,因为我还是个新手,所以我将能够更好地理解它。 提前谢谢你。

编辑:对于不熟悉 iPhone 分页视图的人,here 是一个视频示例。

【问题讨论】:

  • 如果只有图像,则图库视图将起作用。
  • 但就像它在 iphone 中可用一样,我希望页面也出现在底部。
  • 我也想要一个滚动控件,它与 android 手机主视图中的滚动一样平滑。就像我们有 5 个屏幕可用,然后我们可以在它们之间移动。我想效仿在我的应用程序中不使用手势监听器..
  • iphone 分页控件默认提供我所说的所有内容。我想知道android是否可以为我们做这件事,而我们不必做所有的工作。如果不可能,那么当然我将不得不编码..因此问题“实现水平滚动的最佳方式”......
  • @user590849 检查我的答案。有一个开源小部件可以完全满足您的需求。

标签: android scroll


【解决方案1】:

GreenDroid 库(有用的 Android 组件的集合)最近添加了一个 PagedView 类,它可以完全满足您的需求。它还包括一个类似于 iOS 点的 PageIndicator。

它使用适配器系统,类似于 ListView(具有高效的视图重用等),我在此模式的任何其他实现中都没有见过。


源代码https://github.com/cyrilmottier/GreenDroid

演示应用程序https://market.android.com/details?id=com.cyrilmottier.android.gdcatalog

【讨论】:

    【解决方案2】:

    如果您对复制主屏幕样式的分页控件特别感兴趣,请查看this,源代码为Github。它甚至包括代码来做底部的小页面点。

    【讨论】:

      【解决方案3】:

      我之前发布过类似 Android 主屏幕的代码:Developing an Android Homescreen

      请注意,这将使整个屏幕在页面之间翻转,如果您只想翻转部分屏幕(如您链接到的视频上所示),它将不起作用。

      首先是源代码

      package com.matthieu.launcher;
      
      import android.content.Context;
      import android.util.Log;
      import android.content.res.TypedArray;
      import android.util.AttributeSet;
      import android.view.MotionEvent;
      import android.view.VelocityTracker;
      import android.view.View;
      import android.view.ViewGroup;
      import android.view.ViewConfiguration;
      import android.widget.Scroller;
      
      public class DragableSpace extends ViewGroup {
          private Scroller mScroller;
          private VelocityTracker mVelocityTracker;
      
          private int mScrollX = 0;
          private int mCurrentScreen = 0;
      
          private float mLastMotionX;
      
          private static final String LOG_TAG = "DragableSpace";
      
          private static final int SNAP_VELOCITY = 1000;
      
          private final static int TOUCH_STATE_REST = 0;
          private final static int TOUCH_STATE_SCROLLING = 1;
      
          private int mTouchState = TOUCH_STATE_REST;
      
          private int mTouchSlop = 0;
      
          public DragableSpace(Context context) {
              super(context);
              mScroller = new Scroller(context);
      
              mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
      
              this.setLayoutParams(new ViewGroup.LayoutParams(
                          ViewGroup.LayoutParams.WRAP_CONTENT,
                          ViewGroup.LayoutParams.FILL_PARENT));
          }
      
          public DragableSpace(Context context, AttributeSet attrs) {
              super(context, attrs);
              mScroller = new Scroller(context);
      
              mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
      
              this.setLayoutParams(new ViewGroup.LayoutParams(
                          ViewGroup.LayoutParams.WRAP_CONTENT ,
                          ViewGroup.LayoutParams.FILL_PARENT));
      
              TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.DragableSpace);
              mCurrentScreen = a.getInteger(R.styleable.DragableSpace_default_screen, 0);
          }
      
          @Override
          public boolean onInterceptTouchEvent(MotionEvent ev) {
              /*
               * This method JUST determines whether we want to intercept the motion.
               * If we return true, onTouchEvent will be called and we do the actual
               * scrolling there.
               */
      
              /*
               * Shortcut the most recurring case: the user is in the dragging state
               * and he is moving his finger. We want to intercept this motion.
               */
              final int action = ev.getAction();
              if ((action == MotionEvent.ACTION_MOVE)
                      && (mTouchState != TOUCH_STATE_REST)) {
                  return true;
                      }
      
              final float x = ev.getX();
      
              switch (action) {
                  case MotionEvent.ACTION_MOVE:
                      /*
                       * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
                       * whether the user has moved far enough from his original down touch.
                       */
      
                      /*
                       * Locally do absolute value. mLastMotionX is set to the y value
                       * of the down event.
                       */
                      final int xDiff = (int) Math.abs(x - mLastMotionX);
      
                      boolean xMoved = xDiff > mTouchSlop;
      
                      if (xMoved) {
                          // Scroll if the user moved far enough along the X axis
                          mTouchState = TOUCH_STATE_SCROLLING;
                      }
                      break;
      
                  case MotionEvent.ACTION_DOWN:
                      // Remember location of down touch
                      mLastMotionX = x;
      
                      /*
                       * If being flinged and user touches the screen, initiate drag;
                       * otherwise don't.  mScroller.isFinished should be false when
                       * being flinged.
                       */
                      mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
                      break;
      
                  case MotionEvent.ACTION_CANCEL:
                  case MotionEvent.ACTION_UP:
                      // Release the drag
                      mTouchState = TOUCH_STATE_REST;
                      break;
              }
      
              /*
               * The only time we want to intercept motion events is if we are in the
               * drag mode.
               */
              return mTouchState != TOUCH_STATE_REST;
          }
      
          @Override
          public boolean onTouchEvent(MotionEvent event) {
      
              if (mVelocityTracker == null) {
                  mVelocityTracker = VelocityTracker.obtain();
              }
              mVelocityTracker.addMovement(event);
      
              final int action = event.getAction();
              final float x = event.getX();
      
              switch (action) {
                  case MotionEvent.ACTION_DOWN:
                      Log.i(LOG_TAG, "event : down");
                      /*
                       * If being flinged and user touches, stop the fling. isFinished
                       * will be false if being flinged.
                       */
                      if (!mScroller.isFinished()) {
                          mScroller.abortAnimation();
                      }
      
                      // Remember where the motion event started
                      mLastMotionX = x;
                      break;
                  case MotionEvent.ACTION_MOVE:
                      // Log.i(LOG_TAG,"event : move");
                      // if (mTouchState == TOUCH_STATE_SCROLLING) {
                      // Scroll to follow the motion event
                      final int deltaX = (int) (mLastMotionX - x);
                      mLastMotionX = x;
      
                      //Log.i(LOG_TAG, "event : move, deltaX " + deltaX + ", mScrollX " + mScrollX);
      
                      if (deltaX < 0) {
                          if (mScrollX > 0) {
                              scrollBy(Math.max(-mScrollX, deltaX), 0);
                          }
                      } else if (deltaX > 0) {
                          final int availableToScroll = getChildAt(getChildCount() - 1)
                              .getRight()
                              - mScrollX - getWidth();
                          if (availableToScroll > 0) {
                              scrollBy(Math.min(availableToScroll, deltaX), 0);
                          }
                      }
                      // }
                      break;
                  case MotionEvent.ACTION_UP:
                      Log.i(LOG_TAG, "event : up");
                      // if (mTouchState == TOUCH_STATE_SCROLLING) {
                      final VelocityTracker velocityTracker = mVelocityTracker;
                      velocityTracker.computeCurrentVelocity(1000);
                      int velocityX = (int) velocityTracker.getXVelocity();
      
                      if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
                          // Fling hard enough to move left
                          snapToScreen(mCurrentScreen - 1);
                      } else if (velocityX < -SNAP_VELOCITY
                              && mCurrentScreen < getChildCount() - 1) {
                          // Fling hard enough to move right
                          snapToScreen(mCurrentScreen + 1);
                      } else {
                          snapToDestination();
                      }
      
                      if (mVelocityTracker != null) {
                          mVelocityTracker.recycle();
                          mVelocityTracker = null;
                      }
                      // }
                      mTouchState = TOUCH_STATE_REST;
                      break;
                  case MotionEvent.ACTION_CANCEL:
                      Log.i(LOG_TAG, "event : cancel");
                      mTouchState = TOUCH_STATE_REST;
              }
              mScrollX = this.getScrollX();
      
              return true;
          }
      
          private void snapToDestination() {
              final int screenWidth = getWidth();
              final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
              Log.i(LOG_TAG, "from des");
              snapToScreen(whichScreen);
          }
      
          public void snapToScreen(int whichScreen) {         
              Log.i(LOG_TAG, "snap To Screen " + whichScreen);
              mCurrentScreen = whichScreen;
              final int newX = whichScreen * getWidth();
              final int delta = newX - mScrollX;
              mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2);             
              invalidate();
          }
      
          public void setToScreen(int whichScreen) {
              Log.i(LOG_TAG, "set To Screen " + whichScreen);
              mCurrentScreen = whichScreen;
              final int newX = whichScreen * getWidth();
              mScroller.startScroll(newX, 0, 0, 0, 10);             
              invalidate();
          }
      
          @Override
          protected void onLayout(boolean changed, int l, int t, int r, int b) {
              int childLeft = 0;
      
              final int count = getChildCount();
              for (int i = 0; i < count; i++) {
                  final View child = getChildAt(i);
                  if (child.getVisibility() != View.GONE) {
                      final int childWidth = child.getMeasuredWidth();
                      child.layout(childLeft, 0, childLeft + childWidth, child
                              .getMeasuredHeight());
                      childLeft += childWidth;
                  }
              }
      
          }
      
          @Override
          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
              super.onMeasure(widthMeasureSpec, heightMeasureSpec);
      
              final int width = MeasureSpec.getSize(widthMeasureSpec);
              final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
              if (widthMode != MeasureSpec.EXACTLY) {
                  throw new IllegalStateException("error mode.");
              }
      
              final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
              if (heightMode != MeasureSpec.EXACTLY) {
                  throw new IllegalStateException("error mode.");
              }
      
              // The children are given the same width and height as the workspace
              final int count = getChildCount();
              for (int i = 0; i < count; i++) {
                  getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
              }
              Log.i(LOG_TAG, "moving to screen "+mCurrentScreen);
              scrollTo(mCurrentScreen * width, 0);      
          }  
      
          @Override
          public void computeScroll() {
              if (mScroller.computeScrollOffset()) {
                  mScrollX = mScroller.getCurrX();
                  scrollTo(mScrollX, 0);
                  postInvalidate();
              }
          }
      }
      

      还有布局文件:

      <?xml version="1.0" encoding="utf-8"?>
      <com.matthieu.launcher.DragableSpace xmlns:app="http://schemas.android.com/apk/res/com.matthieu.launcher"
          xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/space"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      app:default_screen="1"
      >
      <include android:id="@+id/left"  layout="@layout/left_screen" />
      <include android:id="@+id/center"  layout="@layout/initial_screen" />
      <include android:id="@+id/right"  layout="@layout/right_screen" />
      </com.matthieu.launcher.DragableSpace>
      

      为了能够在 xml 文件中拥有额外的属性,您希望将其保存在 res/values/attrs.xml 中

      <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <declare-styleable name="DragableSpace">
              <attr name="default_screen" format="integer"/>
          </declare-styleable>
      </resources>
      

      原来的 SO 帖子也有一些 cmets 以使其正常工作...

      【讨论】:

        【解决方案4】:

        这里有一个solution 这个问题有很多有用的代码。

        【讨论】:

          【解决方案5】:
          【解决方案6】:

          就像 dmon 所说,您可以尝试使用 android 主屏幕应用程序试试这个http://code.google.com/p/deezapps-widgets/

          在我看来,这就像 android 的主屏幕的自定义实现......并且像 iphone 一样具有页面控制......我认为这就是你正在寻找的......

          【讨论】:

          • 我可以看到控件,但我无法下载源代码。我不是会员......如果不下载,我无法测试小部件
          • 当然你可以.. 使用 SVN 并从后备箱中检出。
          【解决方案7】:

          我想你想要像 iphone 应用程序开发中的分页控件,在 android 中这个功能可以通过使用 ViewFlipper 获得。它可能会帮助您实现。

          【讨论】:

          • 这是个好主意,但是客户想要滚动功能..如果我通过 viewflipper 做我想做的事,那么我必须输入一个手势监听器来查看要翻转的视图和速率翻转。所有这些都是在 ios 中默认完成的。
          • 然后使用内部使用线性布局的水平滚动视图并将图像设置为线性布局(方向为水平)。对于平滑滚动,请使用滚动视图的各种属性,如 Smoothscroll 等。
          【解决方案8】:

          我已经在此处发布了一些内容:Horizontal "tab"ish scroll between views

          我很幸运地复制了您描述的水平滚动类型导航。

          【讨论】:

            【解决方案9】:

            好吧,你可以看看source code for the android homescreen,因为它是开源的。也许你可以从那里得到一些想法。

            【讨论】:

              【解决方案10】:

              您可以使用HorizontalScrollView 来保存包含所有图像视图的LinearLayout

              【讨论】:

              • 但是滚动很难处理。我的意思是,默认情况下滚动视图不是平滑滚动。我将不得不实现手势监听器,然后像 smoothscrollto 这样的方法 - 实现平滑的水平滚动..
              • 我已经实现了类似的东西......这是我管理过的最好的解决方案
              • 其实Horizo​​ntalScrollView 确实有平滑滚动。这加上LinearLayout 是解决问题的最简单方法,不需要任何子类化。
              • 好的。我只希望当用户从一组图像滚动到另一组图像并在两者之间停止时,他应该自动滚动到可见超过 50% 的屏幕。你能更详细地解释你在说什么,以便我能实现我想要的。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-08-05
              • 2013-06-19
              • 2011-11-12
              • 1970-01-01
              • 2015-04-24
              相关资源
              最近更新 更多