【问题标题】:Android: Enable Scrollbars on Canvas-Based ViewAndroid:在基于 Canvas 的视图上启用滚动条
【发布时间】:2011-02-01 23:40:02
【问题描述】:

我有一个扩展 View 的自定义视图。它显示绘制的形状,并允许用户通过绘制触摸事件通过 onDraw 将这些图形添加到视图中。

我已启用 ScaleGestureDetector,以便用户可以放大特定部分并进行绘图,但由于我使用单点触控进行绘图,他们无法使用手指在放大的视图周围平移。

我尝试为视图启用滚动条,这样当它们放大时,滚动条就会显示出来,并且用户可以使用它来平移...但我根本无法显示滚动条。

本质上,我所做的是在用户放大时在我的ScaleListener 的onScale() 方法中调用View 的awakenScrollBars() 方法,这会触发invalidate()。我通过 XML 和onCreate() 以编程方式启用了滚动条,但我无法触发滚动条可见。这是我的 XML:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.package.name.Canvas
    android:id="@+id/canvas"
    android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"
        android:scrollbars="horizontal|vertical" />
</FrameLayout>

这是我的 onCreate():

// set scrollbars
setHorizontalScrollBarEnabled(true);
setVerticalScrollBarEnabled(true);

在 onDraw 中,我可以通过 isHorizontalScrollBarEnabled()isVerticalScrollBarEnabled() 验证滚动条是否启用,onScale() 中的 awakenScrollBars() 返回 true,但滚动条不可见。

关于如何进行的任何建议?在 ScrollView 布局中包含自定义视图似乎不是一个选项,因为它只支持垂直滚动。

谢谢,

保罗

【问题讨论】:

    标签: android scroll android-canvas


    【解决方案1】:

    如果您以编程方式创建自定义视图,则答案如下: 为了在自定义视图类上显示滚动条,应该在初始化期间调用“initializeScrollbars”方法(例如在构造函数中)。

    此方法采用 TypedArray 类型的一个非常晦涩的参数。要获得合适的 TypedArray 实例,您需要创建自定义样式条目 - 只需在“res\values”目录中创建文件“attrs.xml”,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <declare-styleable name="View">
        <attr name="android:background"/>
        <attr name="android:clickable"/>
        <attr name="android:contentDescription"/>
        <attr name="android:drawingCacheQuality"/>
        <attr name="android:duplicateParentState"/>
        <attr name="android:fadeScrollbars"/>
        <attr name="android:fadingEdge"/>
        <attr name="android:fadingEdgeLength"/>
        <attr name="android:fitsSystemWindows"/>
        <attr name="android:focusable"/>
        <attr name="android:focusableInTouchMode"/>
        <attr name="android:hapticFeedbackEnabled"/>
        <attr name="android:id"/>
        <attr name="android:isScrollContainer"/>
        <attr name="android:keepScreenOn"/>
        <attr name="android:longClickable"/>
        <attr name="android:minHeight"/>
        <attr name="android:minWidth"/>
        <attr name="android:nextFocusDown"/>
        <attr name="android:nextFocusLeft"/>
        <attr name="android:nextFocusRight"/>
        <attr name="android:nextFocusUp"/>
        <attr name="android:onClick"/>
        <attr name="android:padding"/>
        <attr name="android:paddingBottom"/>
        <attr name="android:paddingLeft"/>
        <attr name="android:paddingRight"/>
        <attr name="android:paddingTop"/>
        <attr name="android:saveEnabled"/>
        <attr name="android:scrollX"/>
        <attr name="android:scrollY"/>
        <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>
        <attr name="android:scrollbarAlwaysDrawVerticalTrack"/>
        <attr name="android:scrollbarDefaultDelayBeforeFade"/>
        <attr name="android:scrollbarFadeDuration"/>
        <attr name="android:scrollbarSize"/>
        <attr name="android:scrollbarStyle"/>
        <attr name="android:scrollbarThumbHorizontal"/>
        <attr name="android:scrollbarThumbVertical"/>
        <attr name="android:scrollbarTrackHorizontal"/>
        <attr name="android:scrollbarTrackVertical"/>
        <attr name="android:scrollbars"/>
        <attr name="android:soundEffectsEnabled"/>
        <attr name="android:tag"/>
        <attr name="android:visibility"/>
    </declare-styleable>
    </resources>
    

    另外完整的滚动条初始化代码是(将它放在您的自定义视图构造函数中):

    setHorizontalScrollBarEnabled(true);
    setVerticalScrollBarEnabled(true);
    
    TypedArray a = context.obtainStyledAttributes(R.styleable.View);
    initializeScrollbars(a);
    a.recycle();
    

    附:该解决方案已在 Android 2.0 上测试

    我忘了补充: “computeVerticalScrollRange”和“computeHorizo​​ntalScrollRange”方法也应该被覆盖。它们应该只返回画布的假想宽度和高度。

    【讨论】:

    • 感谢您的回复...为了避免滚动问题,我采取了不同的方向,因此不确定何时可以查看此内容,但我打算在某个时候这样做。
    • 为我工作,但我不需要资源文件中的任何 值。我怀疑如果您可以获取任何 TypedArray 实例,它就会起作用。
    • 其实可以替换资源文件,直接使用final TypedArray a = context.getTheme().obtainStyledAttributes(new int[0]); initializeScrollbars(a);
    • 不要忘记 TypedArray 应该是在 try/finally 块中收集的资源
    • 获取 TypedArray 的另一种方法是监视 ScrollView。我在 MyScrollView 的构造函数中有这个。好的,我放弃格式化代码。采取以下并手动格式化 --> new ScrollView(context) { @Override protected void initializeScrollbars(TypedArray a) { MyScrollView.this.initializeScrollbars(a); } };
    【解决方案2】:

    对于那些试图将滚动条添加到自定义 ViewGroup(而不是 View)或其子类之一的人,除了执行 Ruslan Yanchyshyn 在他们的上面回答。

    更多信息可以在这个question和我的answer看到。

    【讨论】:

      【解决方案3】:

      众所周知,Android 视图具有滚动功能。

      为了显示滚动条,需要做两件事。

      1. 在视图的 xml 声明中添加 android:scrollbars="horizo​​ntal|vertical"
      2. 重写View类的computeHorizo​​ntalScrollRange/computeVerticalScrollRange方法,使得返回值大于computeHorizo​​ntalScrollExtent/computeVerticalScrollExtent方法的返回值

      之后,当您调用 scrollTo 或 scrollBy 方法时,滚动条应该会自动显示。

      对不起,我的英语很差,如果有任何拼写或语法错误。 你可以试试这个:
      activity_main.xml

      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:id="@+id/LinearLayout1"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:orientation="vertical"
      android:paddingBottom="10dp"
      android:paddingLeft="10dp"
      android:paddingRight="10dp"
      android:paddingTop="10dp"
      tools:context=".MainActivity" >
      
      <com.netease.test.testscroll.ScrollImageView
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#FFCCCCCC"
          android:scrollbars="horizontal|vertical"
          android:src="@drawable/pp" />
      
      </LinearLayout>
      

      ScrollImageView 是一个自定义视图,你可以这样实现:

      public class ScrollImageView extends ImageView {
          static final String TAG = "ScrollImageView";
          private Rect mContentRect = new Rect();
          GestureDetector mDetector;
          OnGestureListener mListener = new GestureDetector.SimpleOnGestureListener() {
              @Override
              public boolean onScroll(MotionEvent e1, MotionEvent e2,
                      float distanceX, float distanceY) {
                  Log.i("onScroll", "before:distanceX = " + distanceX
                          + ", distanceY = " + distanceY);
                  scrollBy((int)distanceX, (int)distanceY);
                  // boolean value = awakenScrollBars();
                  Log.i("onScroll", "after:current ScrollX=" + getScrollX()
                          + ", ScrollY=" + getScrollY());
                  return true;
              }
          };
          public ScrollImageView(Context context) {
              this(context, null);
          }
          public ScrollImageView(Context context, AttributeSet attrs) {
              this(context, attrs, 0);
          }
          public ScrollImageView(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
              setScaleType(ScaleType.MATRIX);
              mDetector = new GestureDetector(getContext(), mListener);
          }
          @Override
          public boolean onTouchEvent(MotionEvent event) {
              mDetector.onTouchEvent(event);
              return true;
          }
          @Override
          protected void onSizeChanged(int w, int h, int oldw, int oldh) {
              super.onSizeChanged(w, h, oldw, oldh);
              mContentRect.set(getPaddingLeft(), getPaddingTop(), getWidth()
                      - getPaddingRight(), getHeight() - getPaddingBottom());
          }
          @Override
          protected int computeHorizontalScrollRange() {
              return getDrawable().getIntrinsicWidth();
          }
          @Override
          protected int computeHorizontalScrollExtent() {
              return mContentRect.width();
          }
          @Override
          protected int computeHorizontalScrollOffset() {
              return Math.max(0, getScrollX());
          }
          private int getScrollRangeX() {
              return computeHorizontalScrollRange() - computeHorizontalScrollExtent();
          }
          @Override
          protected int computeVerticalScrollRange() {
              return getDrawable().getIntrinsicHeight();
          }
          @Override
          protected int computeVerticalScrollExtent() {
              return mContentRect.height();
          }
          @Override
          protected int computeVerticalScrollOffset() {
              return Math.max(0, getScrollY());
          }
          private int getScrollRangeY() {
              return computeVerticalScrollRange() - computeVerticalScrollExtent();
          }
      }
      

      指定一个名为“pp”的大png,然后滚动图像,您应该会看到滚动条。

      【讨论】:

        【解决方案4】:

        “initializeScrollbars”方法已从 API 级别 21 开始删除。

            final TypedArray a = context.obtainStyledAttributes(R.styleable.View);
            initializeScrollbars(a);//removed method in Lollipop.
            a.recycle();
        

        看。 https://developer.android.com/sdk/api_diff/21/changes/android.view.View.htmlinitializeScrollbars is undefined?

        仅供参考。我们不能再在 Lollipop 中使用方法 initializeScrollbars(a)。

        【讨论】:

          猜你喜欢
          • 2011-11-10
          • 1970-01-01
          • 2016-06-25
          • 1970-01-01
          • 2011-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多