Android: Enable Scrollbars on Canvas-Based View

后端 未结 4 777
我寻月下人不归
我寻月下人不归 2020-12-06 03:53

I have a custom view that extends View. It displays drawn shapes, and allows the user to add to those drawings via drawing touch events to the View via onDraw.

I\'v

相关标签:
4条回答
  • 2020-12-06 04:13

    If you are creating your custom view programatically, then the answer is the following: in order to show scrollbars on custom view class, the method "initializeScrollbars" should be called during initialization (in constructor for example).

    This method takes one very obscure parameter of type TypedArray. To get suitable TypedArray instance you need to create custom styleable entry - just create file "attrs.xml" in your "res\values" directory with the following content:

    <?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>
    

    Also the complete scrollbars initialization code is (place it on your custom view constructor):

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

    P.S. the solution was tested on Android 2.0

    I have forgot to add: also "computeVerticalScrollRange" and "computeHorizontalScrollRange" methods should be overridden. They should just return imaginary width and height of the canvas.

    0 讨论(0)
  • 2020-12-06 04:18

    The method "initializeScrollbars" has been removed from API level 21 onwards.

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

    See. https://developer.android.com/sdk/api_diff/21/changes/android.view.View.html. initializeScrollbars is undefined?

    FYI. We cannot use the method initializeScrollbars(a) anymore in Lollipop.

    0 讨论(0)
  • 2020-12-06 04:30

    For those of you trying to add scrollbars to a custom ViewGroup (rather than View) or one of its subclasses, make sure you add setWillNotDraw(false); to your constructor in addition to doing what Ruslan Yanchyshyn said in their answer above.

    More info can be seen in this question and my answer.

    0 讨论(0)
  • 2020-12-06 04:32

    As we all known, the Android view has the ability to scroll.

    There are two things to do in order to show the scroll bars.

    1. add android:scrollbars="horizontal|vertical" in the xml declaration of your view
    2. override the computeHorizontalScrollRange/ computeVerticalScrollRange method of the View class, making the return values large than the return values of computeHorizontalScrollExtent/computeVerticalScrollExtent method

    After that the scrollbars should automatically show when you invoke the scrollTo or scrollBy method.

    Sorry for my poor English, if there is any spell or grammar mistakes. you can try this out:
    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 is a custom view, you can implement like this:

    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();
        }
    }
    

    specify a large png named "pp", and just scroll the image, you should see the scrollbars.

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