如何使“滚动条”出现在左侧?

时间:2022-04-20 21:16:30

In an Android ListView, how can I make the scrollbar appear on the left side?

在Android ListView中,如何让滚动条显示在左侧?

4 个解决方案

#1


48  

example:

mView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);

#2


3  

You can move the scrollbar position to the left for any View using View.SCROLLBAR_POSITION_LEFT.

您可以使用View.SCROLLBAR_​​POSITION_LEFT将滚动条位置向左移动到任何视图。

#3


3  

As the other two answers have mentioned, one possibility is using View.setVerticalScrollbarPosition() with SCROLLBAR_POSITION_LEFT. However, one giant caveat is that this requires API level 11+, which at time of writing accounts for fewer than 10% of Android installations. For most applications, that's not acceptable.

正如其他两个答案所提到的,一种可能性是使用带有SCROLLBAR_​​POSITION_LEFT的View.setVerticalScrollbarPosition()。然而,一个巨大的警告是,这需要API级别11+,在撰写本文时占Android安装的比例不到10%。对于大多数应用程序,这是不可接受的。

One possibility that comes to mind to accomplish what you want on older versions of Android would be to do something very kludgy: turn off the scroll bar, mirror your main layout with a narrow layout to the left of it, just wide enough to fit a scroll bar, and manually scroll the left view with scrollyBy() as your main view is scrolled (by overriding onScrollChanged()).

想到在旧版Android上实现你想要的东西的一种可能性就是做一些非常糟糕的事情:关闭滚动条,用一个狭窄的布局镜像你的主要布局到它的左边,宽度足够适合一个滚动条,并在滚动主视图时通过scrollyBy()手动滚动左视图(通过覆盖onScrollChanged())。

That said, I wouldn't actually recommend that unless there's a very compelling reason to move the scroll bar to the left. In most cases, you want your app to fit in and behave like any other app on the device by just letting Android follow its defaults.

也就是说,除非有一个非常令人信服的理由将滚动条移动到左侧,否则我实际上不会建议这样做。在大多数情况下,只需让Android遵循其默认设置,您就希望自己的应用适合设备上的任何其他应用。

#4


1  

Try my hack, seems to work at least on 2.2 and above.

尝试我的黑客,似乎至少在2.2及以上的工作。

import java.lang.reflect.Field;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ListView;

/**
 * This class fixes the lack of setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT) before API level 11
 * @author Genadz Batsyan
 */

public class ListViewWithLeftScrollBar extends ListView {
    private static final String LOG_TAG = ListViewWithLeftScrollBar.class.getSimpleName();
    private static final boolean DEBUG = true;

    private boolean patchInvalidate;

    public ListViewWithLeftScrollBar(Context context) {
        super(context);
        moveVerticalScrollbarToTheLeft();
    }

    public ListViewWithLeftScrollBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        moveVerticalScrollbarToTheLeft();
    }

    public ListViewWithLeftScrollBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        moveVerticalScrollbarToTheLeft();
    }

    @Override
    public void invalidate(Rect r) {
        invalidate(r.left, r.top, r.right, r.bottom);
    }

    @Override
    public void invalidate(int left, int top, int right, int bottom) {
        int width = right - left;
        if (DEBUG) log("invalidate original w:"+ getWidth() +" h:"+ getHeight()+"  rect:"+left+", "+top+", "+right+", "+bottom);
        if (patchInvalidate && right == getWidth() && top == 0 && bottom == getHeight() && width < 30) {
            // The above condition should ensure that ListView is VERY likely to be invalidating the scrollbar.
            // In fact ListView appears to not invalidate anything except the scrollbar, ever.
            left = 0;
            right = left + width;
            if (DEBUG) log("invalidate patched  w:"+ getWidth() +" h:"+ getHeight()+"  rect:"+left+", "+top+", "+right+", "+bottom);
        }
        super.invalidate(left, top, right, bottom);
    }

    private void moveVerticalScrollbarToTheLeft() {
        try {
            if (DEBUG) log("moveVerticalScrollbarToTheLeft: Trying API Level >=11");
            tryApiLevel11();
            if (DEBUG) log("moveVerticalScrollbarToTheLeft: API Level >=11 success");
        } catch (Throwable t1) {
            if (DEBUG) {
                log("moveVerticalScrollbarToTheLeft: API Level >=11 FAILED");
                t1.printStackTrace();
            }
            try {
                if (DEBUG) log("moveVerticalScrollbarToTheLeft: Trying hack for API Level <11");
                tryApiLevelPre11();
                patchInvalidate = true;
                if (DEBUG) log("moveVerticalScrollbarToTheLeft: API Level <11 hack success");
            } catch (Throwable t2) {
                if (DEBUG) {
                    log("moveVerticalScrollbarToTheLeft: API Level <11 hack FAILED");
                    t2.printStackTrace();
                }
            }
        }
    }

    @SuppressLint("NewApi")
    private void tryApiLevel11() {
        setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);
    }

    private void tryApiLevelPre11() throws Exception {
        Class<?> viewClass = View.class;

        Field scrollCacheField = viewClass.getDeclaredField("mScrollCache");
        scrollCacheField.setAccessible(true);
        Object scrollCache = scrollCacheField.get(this);
        if (DEBUG) log("scrollCache: "+ scrollCache);

        Field scrollBarField = scrollCache.getClass().getDeclaredField("scrollBar");
        scrollBarField.setAccessible(true);
        Object scrollBar = scrollBarField.get(scrollCache);
        if (DEBUG) log("scrollBar: "+ scrollBar);

        Field verticalThumbField = scrollBar.getClass().getDeclaredField("mVerticalThumb");
        verticalThumbField.setAccessible(true);
        Object verticalThumb = verticalThumbField.get(scrollBar);
        if (DEBUG) log("verticalThumb: "+ verticalThumb);

        Drawable verticalThumbDrawable = (Drawable) verticalThumb;
        Drawable replacementVerticalThumbDrawable = new LayerDrawable(new Drawable[]{ verticalThumbDrawable }) {
            @Override
            public void setBounds(int left, int top, int right, int bottom) {
                if (DEBUG) log("setBounds original: "+left+", "+top+", "+right+", "+bottom);
                int width = right - left;
                left = 0;
                right = left + width;
                if (DEBUG) log("setBounds patched:  "+left+", "+top+", "+right+", "+bottom);
                super.setBounds(left, top, right, bottom);
            }       
        };
        verticalThumbField.set(scrollBar, replacementVerticalThumbDrawable);
    }

    private static void log(String msg) {
        Log.d(LOG_TAG, msg);
    }
}

#1


48  

example:

mView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);

#2


3  

You can move the scrollbar position to the left for any View using View.SCROLLBAR_POSITION_LEFT.

您可以使用View.SCROLLBAR_​​POSITION_LEFT将滚动条位置向左移动到任何视图。

#3


3  

As the other two answers have mentioned, one possibility is using View.setVerticalScrollbarPosition() with SCROLLBAR_POSITION_LEFT. However, one giant caveat is that this requires API level 11+, which at time of writing accounts for fewer than 10% of Android installations. For most applications, that's not acceptable.

正如其他两个答案所提到的,一种可能性是使用带有SCROLLBAR_​​POSITION_LEFT的View.setVerticalScrollbarPosition()。然而,一个巨大的警告是,这需要API级别11+,在撰写本文时占Android安装的比例不到10%。对于大多数应用程序,这是不可接受的。

One possibility that comes to mind to accomplish what you want on older versions of Android would be to do something very kludgy: turn off the scroll bar, mirror your main layout with a narrow layout to the left of it, just wide enough to fit a scroll bar, and manually scroll the left view with scrollyBy() as your main view is scrolled (by overriding onScrollChanged()).

想到在旧版Android上实现你想要的东西的一种可能性就是做一些非常糟糕的事情:关闭滚动条,用一个狭窄的布局镜像你的主要布局到它的左边,宽度足够适合一个滚动条,并在滚动主视图时通过scrollyBy()手动滚动左视图(通过覆盖onScrollChanged())。

That said, I wouldn't actually recommend that unless there's a very compelling reason to move the scroll bar to the left. In most cases, you want your app to fit in and behave like any other app on the device by just letting Android follow its defaults.

也就是说,除非有一个非常令人信服的理由将滚动条移动到左侧,否则我实际上不会建议这样做。在大多数情况下,只需让Android遵循其默认设置,您就希望自己的应用适合设备上的任何其他应用。

#4


1  

Try my hack, seems to work at least on 2.2 and above.

尝试我的黑客,似乎至少在2.2及以上的工作。

import java.lang.reflect.Field;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ListView;

/**
 * This class fixes the lack of setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT) before API level 11
 * @author Genadz Batsyan
 */

public class ListViewWithLeftScrollBar extends ListView {
    private static final String LOG_TAG = ListViewWithLeftScrollBar.class.getSimpleName();
    private static final boolean DEBUG = true;

    private boolean patchInvalidate;

    public ListViewWithLeftScrollBar(Context context) {
        super(context);
        moveVerticalScrollbarToTheLeft();
    }

    public ListViewWithLeftScrollBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        moveVerticalScrollbarToTheLeft();
    }

    public ListViewWithLeftScrollBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        moveVerticalScrollbarToTheLeft();
    }

    @Override
    public void invalidate(Rect r) {
        invalidate(r.left, r.top, r.right, r.bottom);
    }

    @Override
    public void invalidate(int left, int top, int right, int bottom) {
        int width = right - left;
        if (DEBUG) log("invalidate original w:"+ getWidth() +" h:"+ getHeight()+"  rect:"+left+", "+top+", "+right+", "+bottom);
        if (patchInvalidate && right == getWidth() && top == 0 && bottom == getHeight() && width < 30) {
            // The above condition should ensure that ListView is VERY likely to be invalidating the scrollbar.
            // In fact ListView appears to not invalidate anything except the scrollbar, ever.
            left = 0;
            right = left + width;
            if (DEBUG) log("invalidate patched  w:"+ getWidth() +" h:"+ getHeight()+"  rect:"+left+", "+top+", "+right+", "+bottom);
        }
        super.invalidate(left, top, right, bottom);
    }

    private void moveVerticalScrollbarToTheLeft() {
        try {
            if (DEBUG) log("moveVerticalScrollbarToTheLeft: Trying API Level >=11");
            tryApiLevel11();
            if (DEBUG) log("moveVerticalScrollbarToTheLeft: API Level >=11 success");
        } catch (Throwable t1) {
            if (DEBUG) {
                log("moveVerticalScrollbarToTheLeft: API Level >=11 FAILED");
                t1.printStackTrace();
            }
            try {
                if (DEBUG) log("moveVerticalScrollbarToTheLeft: Trying hack for API Level <11");
                tryApiLevelPre11();
                patchInvalidate = true;
                if (DEBUG) log("moveVerticalScrollbarToTheLeft: API Level <11 hack success");
            } catch (Throwable t2) {
                if (DEBUG) {
                    log("moveVerticalScrollbarToTheLeft: API Level <11 hack FAILED");
                    t2.printStackTrace();
                }
            }
        }
    }

    @SuppressLint("NewApi")
    private void tryApiLevel11() {
        setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_LEFT);
    }

    private void tryApiLevelPre11() throws Exception {
        Class<?> viewClass = View.class;

        Field scrollCacheField = viewClass.getDeclaredField("mScrollCache");
        scrollCacheField.setAccessible(true);
        Object scrollCache = scrollCacheField.get(this);
        if (DEBUG) log("scrollCache: "+ scrollCache);

        Field scrollBarField = scrollCache.getClass().getDeclaredField("scrollBar");
        scrollBarField.setAccessible(true);
        Object scrollBar = scrollBarField.get(scrollCache);
        if (DEBUG) log("scrollBar: "+ scrollBar);

        Field verticalThumbField = scrollBar.getClass().getDeclaredField("mVerticalThumb");
        verticalThumbField.setAccessible(true);
        Object verticalThumb = verticalThumbField.get(scrollBar);
        if (DEBUG) log("verticalThumb: "+ verticalThumb);

        Drawable verticalThumbDrawable = (Drawable) verticalThumb;
        Drawable replacementVerticalThumbDrawable = new LayerDrawable(new Drawable[]{ verticalThumbDrawable }) {
            @Override
            public void setBounds(int left, int top, int right, int bottom) {
                if (DEBUG) log("setBounds original: "+left+", "+top+", "+right+", "+bottom);
                int width = right - left;
                left = 0;
                right = left + width;
                if (DEBUG) log("setBounds patched:  "+left+", "+top+", "+right+", "+bottom);
                super.setBounds(left, top, right, bottom);
            }       
        };
        verticalThumbField.set(scrollBar, replacementVerticalThumbDrawable);
    }

    private static void log(String msg) {
        Log.d(LOG_TAG, msg);
    }
}