如何在Android中制作垂直的SeekBar?

发布于 2024-09-11 05:54:57 字数 86 浏览 9 评论 0原文

SeekBar 可以是垂直的吗?我不太擅长UI设计,所以如何让SeekBar更漂亮,请给我一些模板和例子。

Can a SeekBar be vertical? I am not very good at UI design, so how can I make the SeekBar more beautiful, please give me some templates and examples.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(19

李白 2024-09-18 05:54:58

尝试:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" > 

<SeekBar 
    android:id="@+id/seekBar1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:rotation="270" 
    /> 

</RelativeLayout> 

Try:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" > 

<SeekBar 
    android:id="@+id/seekBar1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:rotation="270" 
    /> 

</RelativeLayout> 
听你说爱我 2024-09-18 05:54:58

我使用了 Selva 的解决方案,但遇到了两种问题:

  • OnSeekbarChangeListener 无法正常工作
  • 以编程方式设置进度无法正常工作。

我解决了这两个问题。 找到解决方案(在我自己的项目包中)

您可以在https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/components/VerticalSeekBar.java< /a>

I used Selva's solution but had two kinds of issues:

  • OnSeekbarChangeListener did not work properly
  • Setting progress programmatically did not work properly.

I fixed these two issues. You can find the solution (within my own project package) at

https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/components/VerticalSeekBar.java

多情出卖 2024-09-18 05:54:58

我们使用 android:rotation="270" 制作了一个垂直的 SeekBar:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/camera_sv_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/camera_lv_expose"  
        android:layout_width="32dp"
        android:layout_height="200dp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="15dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/camera_tv_expose"
            android:layout_width="32dp"
            android:layout_height="20dp"
            android:textColor="#FFFFFF"
            android:textSize="15sp"
            android:gravity="center"/>

        <FrameLayout
            android:layout_width="32dp"
            android:layout_height="180dp"
            android:orientation="vertical">

            <SeekBar
                android:id="@+id/camera_sb_expose"
                android:layout_width="180dp"
                android:layout_height="32dp" 
                android:layout_gravity="center"
                android:rotation="270"/>

        </FrameLayout>

    </LinearLayout>

    <TextView
        android:id="@+id/camera_tv_help"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"
        android:text="@string/camera_tv"
        android:textColor="#FFFFFF" />

</RelativeLayout>

相机曝光补偿的屏幕截图:

在此处输入图像描述

We made a vertical SeekBar by using android:rotation="270":

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView
        android:id="@+id/camera_sv_preview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout
        android:id="@+id/camera_lv_expose"  
        android:layout_width="32dp"
        android:layout_height="200dp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="15dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/camera_tv_expose"
            android:layout_width="32dp"
            android:layout_height="20dp"
            android:textColor="#FFFFFF"
            android:textSize="15sp"
            android:gravity="center"/>

        <FrameLayout
            android:layout_width="32dp"
            android:layout_height="180dp"
            android:orientation="vertical">

            <SeekBar
                android:id="@+id/camera_sb_expose"
                android:layout_width="180dp"
                android:layout_height="32dp" 
                android:layout_gravity="center"
                android:rotation="270"/>

        </FrameLayout>

    </LinearLayout>

    <TextView
        android:id="@+id/camera_tv_help"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"
        android:text="@string/camera_tv"
        android:textColor="#FFFFFF" />

</RelativeLayout>

Screenshot for camera exposure compensation:

enter image description here

冷…雨湿花 2024-09-18 05:54:58

这对我有用,只需将其放入您想要的任何布局中即可。

<FrameLayout
    android:layout_width="32dp"
    android:layout_height="192dp">

    <SeekBar
        android:layout_width="192dp"
        android:layout_height="32dp"
        android:layout_gravity="center"
        android:rotation="270" />

</FrameLayout>

This worked for me, just put it into any layout you want to.

<FrameLayout
    android:layout_width="32dp"
    android:layout_height="192dp">

    <SeekBar
        android:layout_width="192dp"
        android:layout_height="32dp"
        android:layout_gravity="center"
        android:rotation="270" />

</FrameLayout>
计㈡愣 2024-09-18 05:54:58

请注意,在我看来,如果更改宽度,拇指宽度不会正确更改。
我没有花时间去修复它,我只是针对我的情况修复了它。这就是我所做的。
无法弄清楚如何联系原始创建者。

public void setThumb(Drawable thumb) {
    if (thumb != null) {
        thumb.setCallback(this);

        // Assuming the thumb drawable is symmetric, set the thumb offset
        // such that the thumb will hang halfway off either edge of the
        // progress bar.
        //This was orginally divided by 2, seems you have to adjust here when you adjust width.
        mThumbOffset = (int)thumb.getIntrinsicHeight();
    }

Note, it appears to me that if you change the width the thumb width does not change correctly.
I didn't take the time to fix it right, i just fixed it for my case. Here is what i did.
Couldn't figure out how to contact the original creator.

public void setThumb(Drawable thumb) {
    if (thumb != null) {
        thumb.setCallback(this);

        // Assuming the thumb drawable is symmetric, set the thumb offset
        // such that the thumb will hang halfway off either edge of the
        // progress bar.
        //This was orginally divided by 2, seems you have to adjust here when you adjust width.
        mThumbOffset = (int)thumb.getIntrinsicHeight();
    }
故事和酒 2024-09-18 05:54:58

将其包装在 FrameLayout 内,这样就不存在大小问题。

  <FrameLayout
                android:layout_width="@dimen/_20dp"
                android:layout_marginStart="@dimen/_15dp"
                android:layout_marginEnd="@dimen/_15dp"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <SeekBar
                    android:layout_width="150dp"
                    android:layout_height="30dp"
                    android:layout_gravity="center"
                    android:rotation="270" />
  </FrameLayout>

Wrap it inside a FrameLayout so that there is no Size issue.

  <FrameLayout
                android:layout_width="@dimen/_20dp"
                android:layout_marginStart="@dimen/_15dp"
                android:layout_marginEnd="@dimen/_15dp"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <SeekBar
                    android:layout_width="150dp"
                    android:layout_height="30dp"
                    android:layout_gravity="center"
                    android:rotation="270" />
  </FrameLayout>
夜光 2024-09-18 05:54:58

使用 EditText 移动拇指时,Vertical Seekbar setProgress 可能不起作用。以下代码可以提供帮助:

    @Override
public synchronized void setProgress(int progress) {
    super.setProgress(progress);
    updateThumb();
}

private void updateThumb() {
    onSizeChanged(getWidth(), getHeight(), 0, 0);
}

此代码段可以在此处找到:
https://stackoverflow.com/a/33064140/2447726

When moving the thumb with an EditText, the Vertical Seekbar setProgress may not work. The following code can help:

    @Override
public synchronized void setProgress(int progress) {
    super.setProgress(progress);
    updateThumb();
}

private void updateThumb() {
    onSizeChanged(getWidth(), getHeight(), 0, 0);
}

This snippet code found here:
https://stackoverflow.com/a/33064140/2447726

把时间冻结 2024-09-18 05:54:58

试试这个

import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;

/**
 * Implementation of an easy vertical SeekBar, based on the normal SeekBar.
 */
public class VerticalSeekBar extends SeekBar {
    /**
     * The angle by which the SeekBar view should be rotated.
     */
    private static final int ROTATION_ANGLE = -90;

    /**
     * A change listener registrating start and stop of tracking. Need an own listener because the listener in SeekBar
     * is private.
     */
    private OnSeekBarChangeListener mOnSeekBarChangeListener;

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @see android.view.View#View(Context)
     */
    public VerticalSeekBar(final Context context) {
        super(context);
    }

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param attrs   The attributes of the XML tag that is inflating the view.
     * @see android.view.View#View(Context, AttributeSet)
     */
    public VerticalSeekBar(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context  The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param attrs    The attributes of the XML tag that is inflating the view.
     * @param defStyle An attribute in the current theme that contains a reference to a style resource that supplies default
     *                 values for the view. Can be 0 to not look for defaults.
     * @see android.view.View#View(Context, AttributeSet, int)
     */
    public VerticalSeekBar(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) {
        super.onSizeChanged(height, width, oldHeight, oldWidth);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final void onDraw(@NonNull final Canvas c) {
        c.rotate(ROTATION_ANGLE);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final void setOnSeekBarChangeListener(final OnSeekBarChangeListener listener) {
        // Do not use super for the listener, as this would not set the fromUser flag properly
        mOnSeekBarChangeListener = listener;
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final boolean onTouchEvent(@NonNull final MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStartTrackingTouch(this);
            }
            break;

        case MotionEvent.ACTION_MOVE:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            break;

        case MotionEvent.ACTION_UP:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
            break;

        case MotionEvent.ACTION_CANCEL:
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
            break;

        default:
            break;
        }

        return true;
    }

    /**
     * Set the progress by the user. (Unfortunately, Seekbar.setProgressInternally(int, boolean) is not accessible.)
     *
     * @param progress the progress.
     * @param fromUser flag indicating if the change was done by the user.
     */
    public final void setProgressInternally(final int progress, final boolean fromUser) {
        if (progress != getProgress()) {
            super.setProgress(progress);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
            }
        }
        onSizeChanged(getWidth(), getHeight(), 0, 0);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final void setProgress(final int progress) {
        setProgressInternally(progress, false);
    }
}

Try this

import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.SeekBar;

/**
 * Implementation of an easy vertical SeekBar, based on the normal SeekBar.
 */
public class VerticalSeekBar extends SeekBar {
    /**
     * The angle by which the SeekBar view should be rotated.
     */
    private static final int ROTATION_ANGLE = -90;

    /**
     * A change listener registrating start and stop of tracking. Need an own listener because the listener in SeekBar
     * is private.
     */
    private OnSeekBarChangeListener mOnSeekBarChangeListener;

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @see android.view.View#View(Context)
     */
    public VerticalSeekBar(final Context context) {
        super(context);
    }

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param attrs   The attributes of the XML tag that is inflating the view.
     * @see android.view.View#View(Context, AttributeSet)
     */
    public VerticalSeekBar(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Standard constructor to be implemented for all views.
     *
     * @param context  The Context the view is running in, through which it can access the current theme, resources, etc.
     * @param attrs    The attributes of the XML tag that is inflating the view.
     * @param defStyle An attribute in the current theme that contains a reference to a style resource that supplies default
     *                 values for the view. Can be 0 to not look for defaults.
     * @see android.view.View#View(Context, AttributeSet, int)
     */
    public VerticalSeekBar(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final void onSizeChanged(final int width, final int height, final int oldWidth, final int oldHeight) {
        super.onSizeChanged(height, width, oldHeight, oldWidth);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final synchronized void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    protected final void onDraw(@NonNull final Canvas c) {
        c.rotate(ROTATION_ANGLE);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final void setOnSeekBarChangeListener(final OnSeekBarChangeListener listener) {
        // Do not use super for the listener, as this would not set the fromUser flag properly
        mOnSeekBarChangeListener = listener;
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final boolean onTouchEvent(@NonNull final MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStartTrackingTouch(this);
            }
            break;

        case MotionEvent.ACTION_MOVE:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            break;

        case MotionEvent.ACTION_UP:
            setProgressInternally(getMax() - (int) (getMax() * event.getY() / getHeight()), true);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
            break;

        case MotionEvent.ACTION_CANCEL:
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onStopTrackingTouch(this);
            }
            break;

        default:
            break;
        }

        return true;
    }

    /**
     * Set the progress by the user. (Unfortunately, Seekbar.setProgressInternally(int, boolean) is not accessible.)
     *
     * @param progress the progress.
     * @param fromUser flag indicating if the change was done by the user.
     */
    public final void setProgressInternally(final int progress, final boolean fromUser) {
        if (progress != getProgress()) {
            super.setProgress(progress);
            if (mOnSeekBarChangeListener != null) {
                mOnSeekBarChangeListener.onProgressChanged(this, progress, fromUser);
            }
        }
        onSizeChanged(getWidth(), getHeight(), 0, 0);
    }

    /*
     * (non-Javadoc) ${see_to_overridden}
     */
    @Override
    public final void setProgress(final int progress) {
        setProgressInternally(progress, false);
    }
}
奢华的一滴泪 2024-09-18 05:54:58

我尝试了很多不同的方法,但最适合我的是。
使用 Seekbar

<FrameLayout
    android:id="@+id/VolumeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/MuteButton"
    android:layout_below="@id/volumeText"
    android:layout_centerInParent="true">
        <SeekBar
        android:id="@+id/volume"
        android:layout_width="500dp"
        android:layout_height="60dp"
        android:layout_gravity="center"
        android:progress="50"
        android:secondaryProgress="40"
        android:progressDrawable="@drawable/seekbar_volume"
        android:secondaryProgressTint="@color/tint_neutral"
        android:thumbTint="@color/tint_neutral"
    />

在 FrameLayout 内和代码中

。在Seekbar上设置Pre Draw回调,您可以在其中更改Seekbar的宽度和高度
我在 C# 中完成了这部分,所以我使用的代码是“

            var volumeSlider = view.FindViewById<SeekBar>(Resource.Id.home_link_volume);

            var volumeFrameLayout = view.FindViewById<FrameLayout>(Resource.Id.linkVolumeFrameLayout);

            void OnPreDrawVolume(object sender, ViewTreeObserver.PreDrawEventArgs e)
            {
                volumeSlider.ViewTreeObserver.PreDraw -= OnPreDrawVolume;
                var h = volumeFrameLayout.Height;
                volumeSlider.Rotation = 270.0f;
                volumeSlider.LayoutParameters.Width = h;
                volumeSlider.RequestLayout();
            }

            volumeSlider.ViewTreeObserver.PreDraw += OnPreDrawVolume;

这里我向 PreDraw 事件添加侦听器”,当其触发时,我删除 PreDraw,以便它不会进入无限循环。

因此,当执行 Pre Draw 时,我获取 FrameLayout 的高度并将其分配给 Seekbar。并将seekbar的旋转设置为270。
由于我的搜索栏位于框架布局内部,并且其重力设置为中心。我不需要担心翻译。由于 Seekbar 始终位于框架布局的中间。

我删除EventHandler的原因是因为seekbar.RequestLayout();将使该事件再次执行。

I tried in many different ways, but the one which worked for me was.
Use Seekbar inside FrameLayout

<FrameLayout
    android:id="@+id/VolumeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/MuteButton"
    android:layout_below="@id/volumeText"
    android:layout_centerInParent="true">
        <SeekBar
        android:id="@+id/volume"
        android:layout_width="500dp"
        android:layout_height="60dp"
        android:layout_gravity="center"
        android:progress="50"
        android:secondaryProgress="40"
        android:progressDrawable="@drawable/seekbar_volume"
        android:secondaryProgressTint="@color/tint_neutral"
        android:thumbTint="@color/tint_neutral"
    />

And in Code.

Setup Pre Draw callback on Seekbar, Where you can change the Width and height of the Seekbar
I did this part in c#, so Code i used was

            var volumeSlider = view.FindViewById<SeekBar>(Resource.Id.home_link_volume);

            var volumeFrameLayout = view.FindViewById<FrameLayout>(Resource.Id.linkVolumeFrameLayout);

            void OnPreDrawVolume(object sender, ViewTreeObserver.PreDrawEventArgs e)
            {
                volumeSlider.ViewTreeObserver.PreDraw -= OnPreDrawVolume;
                var h = volumeFrameLayout.Height;
                volumeSlider.Rotation = 270.0f;
                volumeSlider.LayoutParameters.Width = h;
                volumeSlider.RequestLayout();
            }

            volumeSlider.ViewTreeObserver.PreDraw += OnPreDrawVolume;

Here i Add listener to PreDraw Event and when its triggered, I remove the PreDraw so that it doesnt go into Infinite loop.

So when Pre Draw gets executed, I fetch the Height of FrameLayout and assign it to Seekbar. And set the rotation of seekbar to 270.
As my seekbar is inside frame Layout and its Gravity is set as Center. I dont need to worry about the Translation. As Seekbar always stay in middle of Frame Layout.

Reason i remove EventHandler is because seekbar.RequestLayout(); Will make this event to be executed again.

誰認得朕 2024-09-18 05:54:58

通过使用 RotateLayout,拥有垂直 SeekBar 变得轻而易举。只需将那个可怕的 SeekBar 包裹进去,Bob 就是你的叔叔:

<com.github.rongi.rotate_layout.layout.RotateLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:angle="-90"
    >

        <androidx.appcompat.widget.AppCompatSeekBar
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

</com.github.rongi.rotate_layout.layout.RotateLayout>

https:// github.com/rongi/rotate-layout

By using RotateLayout, having vertical SeekBar is a breeze. Just wrap that horrible SeekBar into it and Bob is your uncle:

<com.github.rongi.rotate_layout.layout.RotateLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:angle="-90"
    >

        <androidx.appcompat.widget.AppCompatSeekBar
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

</com.github.rongi.rotate_layout.layout.RotateLayout>

https://github.com/rongi/rotate-layout

神魇的王 2024-09-18 05:54:58

入门

将这些行添加到 build.gradle。

dependencies {
    compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2'
}

用法

Java 代码

public class TestVerticalSeekbar extends AppCompatActivity {
    private SeekBar volumeControl = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_vertical_seekbar);

        volumeControl = (SeekBar) findViewById(R.id.mySeekBar);

        volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            int progressChanged = 0;

            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressChanged = progress;
            }

            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            public void onStopTrackingTouch(SeekBar seekBar) {
                Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

}

布局 XML

<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
    android:layout_width="wrap_content"
    android:layout_height="150dp">
    <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
        android:id="@+id/mySeekBar"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:max="100"
        android:progress="0"
        android:splitTrack="false"
        app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>

注意:Android N+ 需要 android:splitTrack="false"

Getting started

Add these lines to build.gradle.

dependencies {
    compile 'com.h6ah4i.android.widget.verticalseekbar:verticalseekbar:0.7.2'
}

Usage

Java code

public class TestVerticalSeekbar extends AppCompatActivity {
    private SeekBar volumeControl = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_vertical_seekbar);

        volumeControl = (SeekBar) findViewById(R.id.mySeekBar);

        volumeControl.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            int progressChanged = 0;

            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                progressChanged = progress;
            }

            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            public void onStopTrackingTouch(SeekBar seekBar) {
                Toast.makeText(getApplicationContext(), "seek bar progress:" + progressChanged,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

}

Layout XML

<!-- This library requires pair of the VerticalSeekBar and VerticalSeekBarWrapper classes -->
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
    android:layout_width="wrap_content"
    android:layout_height="150dp">
    <com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
        android:id="@+id/mySeekBar"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:max="100"
        android:progress="0"
        android:splitTrack="false"
        app:seekBarRotation="CW90" /> <!-- Rotation: CW90 or CW270 -->
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>

NOTE: android:splitTrack="false" is required for Android N+.

瑶笙 2024-09-18 05:54:58

就我而言,我使用了一个普通的seekBar,只是翻转了布局。

eekbark_layout.xml - 我的布局包含我们需要使其垂直的seekbar。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<SeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_alignParentBottom="true"/>

</RelativeLayout>

Activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vgfit.seekbarexample.MainActivity">

<View
    android:id="@+id/headerView"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="@color/colorAccent"/>

<View
    android:id="@+id/bottomView"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_alignParentBottom="true"
    android:background="@color/colorAccent"/>

<include
    layout="@layout/seekbar_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/bottomView"
    android:layout_below="@id/headerView"/>

 </RelativeLayout>

在MainActivity中我旋转seekbar_layout:

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.seekbar_layout.*


class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    rootView.post {
        val w = rootView.width
        val h = rootView.height

        rootView.rotation = 270.0f
        rootView.translationX = ((w - h) / 2).toFloat()
        rootView.translationY = ((h - w) / 2).toFloat()

        val lp = rootView.layoutParams as RelativeLayout.LayoutParams
        lp.height = w
        lp.width = h
        rootView.requestLayout()
    }
}
}

因此我们有必要的垂直seekbar:
输入图片此处描述

In my case, I used an ordinary seekBar and just flipped out the layout.

seekbark_layout.xml - my layout that containts seekbar which we need to make vertical.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootView"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<SeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_alignParentBottom="true"/>

</RelativeLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.vgfit.seekbarexample.MainActivity">

<View
    android:id="@+id/headerView"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="@color/colorAccent"/>

<View
    android:id="@+id/bottomView"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_alignParentBottom="true"
    android:background="@color/colorAccent"/>

<include
    layout="@layout/seekbar_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@id/bottomView"
    android:layout_below="@id/headerView"/>

 </RelativeLayout>

And in MainActivity I rotate seekbar_layout:

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.seekbar_layout.*


class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    rootView.post {
        val w = rootView.width
        val h = rootView.height

        rootView.rotation = 270.0f
        rootView.translationX = ((w - h) / 2).toFloat()
        rootView.translationY = ((h - w) / 2).toFloat()

        val lp = rootView.layoutParams as RelativeLayout.LayoutParams
        lp.height = w
        lp.width = h
        rootView.requestLayout()
    }
}
}

As a result we have necessary vertical seekbar:
enter image description here

夏有森光若流苏 2024-09-18 05:54:58

你可以自己做——现在太难了。
这是我项目中的一个示例: https://github.com/AlShevelev/WizardCamera

让我们从设置(attrs.xml)。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ExpositionBar">
        <attr name="button_icon" format="reference" />
        <attr name="button_icon_size" format="dimension" />

        <attr name="stroke_width" format="dimension" />

        <attr name="stroke_color" format="color" />
        <attr name="button_color" format="color" />
        <attr name="button_color_pressed" format="color" />

        <attr name="min_value" format="float" />
        <attr name="max_value" format="float" />
    </declare-styleable>
</resources>

这里有几个实用函数:

fun <T: Comparable<T>>T.fitInRange(range: Range<T>): T =
    when {
        this < range.lower -> range.lower
        this > range.upper -> range.upper
        else -> this
    }

fun Float.reduceToRange(rangeFrom: Range<Float>, rangeTo: Range<Float>): Float =
    when {
        this == rangeFrom.lower -> rangeTo.lower
        this == rangeFrom.upper -> rangeTo.upper
        else -> {
            val placeInRange = (this - rangeFrom.lower) / (rangeFrom.upper - rangeFrom.lower)
            ((rangeTo.upper - rangeTo.lower) * placeInRange) + rangeTo.lower
        }
    }

最后但并非最不重要的 - 垂直搜索栏的类:

class ExpositionBar
@JvmOverloads
constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val drawingRect = RectF(0f, 0f, 0f, 0f)
    private val drawingPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val strokeWidth: Float

    @ColorInt
    private val strokeColor: Int
    @ColorInt
    private val buttonFillColor: Int
    @ColorInt
    private val buttonFillColorPressed: Int

    private val icon: VectorDrawable

    private val valuesRange: Range<Float>

    private var centerX = 0f
    private var minY = 0f
    private var maxY = 0f

    private var buttonCenterY = 0f
    private var buttonRadiusExt = 0f
    private var buttonRadiusInt = 0f
    private var buttonMinY = 0f
    private var buttonMaxY = 0f
    private var buttonCenterBoundsRange = Range(0f, 0f)

    private var iconTranslationX = 0f
    private var iconTranslationY = 0f

    private var isInDragMode = false

    private var onValueChangeListener: ((Float) -> Unit)? = null

    private var oldOutputValue = Float.MIN_VALUE

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpositionBar)

        icon =  typedArray.getDrawable(R.styleable.ExpositionBar_button_icon) as VectorDrawable
        val iconSize = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_button_icon_size, 0)
        icon.setBounds(0, 0, iconSize, iconSize)

        strokeWidth = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_stroke_width, 0).toFloat()
        drawingPaint.strokeWidth = strokeWidth

        strokeColor = typedArray.getColor(R.styleable.ExpositionBar_stroke_color, Color.WHITE)
        buttonFillColor = typedArray.getColor(R.styleable.ExpositionBar_button_color, Color.BLACK)
        buttonFillColorPressed = typedArray.getColor(R.styleable.ExpositionBar_button_color_pressed, Color.BLUE)

        val minValue = typedArray.getFloat(R.styleable.ExpositionBar_min_value, 0f)
        val maxValue = typedArray.getFloat(R.styleable.ExpositionBar_max_value, 0f)
        valuesRange = Range(minValue, maxValue)

        typedArray.recycle()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        drawingRect.right = width.toFloat()
        drawingRect.bottom = height.toFloat()

        buttonCenterY = drawingRect.centerY()

        recalculateDrawingValues()
    }

    override fun onDraw(canvas: Canvas) {
        drawingPaint.color = strokeColor
        drawingPaint.style = Paint.Style.STROKE

        // Draw the center line
        canvas.drawLine(centerX, minY, centerX, buttonMinY, drawingPaint)
        canvas.drawLine(centerX, buttonMaxY, centerX, maxY, drawingPaint)

        // Draw the button
        canvas.drawCircle(centerX, buttonCenterY, buttonRadiusExt, drawingPaint)
        drawingPaint.style = Paint.Style.FILL
        drawingPaint.color = if(isInDragMode) buttonFillColorPressed else buttonFillColor
        canvas.drawCircle(centerX, buttonCenterY, buttonRadiusInt, drawingPaint)

        // Draw button icon
        canvas.translate(iconTranslationX, iconTranslationY)
        icon.draw(canvas)
        canvas.translate(-iconTranslationX, -iconTranslationY)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if(!isEnabled) {
            return false
        }

        when(event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                if(isButtonHit(event.y)){
                    isInDragMode = true
                    invalidate()
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if(isInDragMode) {
                    buttonCenterY = event.y.fitInRange(buttonCenterBoundsRange)
                    recalculateDrawingValues()
                    invalidate()

                    val outputValue = buttonCenterY.reduceToRange(buttonCenterBoundsRange, valuesRange)
                    if (outputValue != oldOutputValue) {
                        onValueChangeListener?.invoke(outputValue)
                        oldOutputValue = outputValue
                    }
                }
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                isInDragMode = false
                invalidate()
            }
        }
        return true
    }

    fun setOnValueChangeListener(listener: ((Float) -> Unit)?) {
        onValueChangeListener = listener
    }

    private fun recalculateDrawingValues() {
        centerX = drawingRect.left + drawingRect.width()/2
        minY = drawingRect.top
        maxY = drawingRect.bottom

        buttonRadiusExt = drawingRect.width() / 2 - strokeWidth / 2
        buttonRadiusInt = buttonRadiusExt - strokeWidth / 2
        buttonMinY = buttonCenterY - buttonRadiusExt
        buttonMaxY = buttonCenterY + buttonRadiusExt

        val buttonCenterMinY = minY + buttonRadiusExt + strokeWidth / 2
        val buttonCenterMaxY = maxY - buttonRadiusExt - strokeWidth / 2
        buttonCenterBoundsRange = Range(buttonCenterMinY, buttonCenterMaxY)

        iconTranslationX = centerX - icon.bounds.width() / 2
        iconTranslationY = buttonCenterY - icon.bounds.height() / 2
    }

    private fun isButtonHit(y: Float): Boolean {
        return y >= buttonMinY && y <= buttonMaxY
    }
}

您可以按如下所示使用它:

<com.shevelev.wizard_camera.main_activity.view.widgets.ExpositionBar
    android:id="@+id/expositionBar"
    android:layout_width="@dimen/mainButtonSize"
    android:layout_height="300dp"
    android:layout_gravity="end|center_vertical"

    android:layout_marginEnd="@dimen/marginNormal"
    android:layout_marginBottom="26dp"

    app:button_icon = "@drawable/ic_brightness"
    app:button_icon_size = "@dimen/toolButtonIconSize"
    app:stroke_width = "@dimen/strokeWidthNormal"
    app:stroke_color = "@color/mainButtonsForeground"
    app:button_color = "@color/mainButtonsBackground"
    app:button_color_pressed = "@color/mainButtonsBackgroundPressed"
    app:min_value="-100"
    app:max_value="100"
/>

瞧!

You can do it by yourself - it's now so difficult.
Here is an example from my project: https://github.com/AlShevelev/WizardCamera

Let start from settings (attrs.xml).

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ExpositionBar">
        <attr name="button_icon" format="reference" />
        <attr name="button_icon_size" format="dimension" />

        <attr name="stroke_width" format="dimension" />

        <attr name="stroke_color" format="color" />
        <attr name="button_color" format="color" />
        <attr name="button_color_pressed" format="color" />

        <attr name="min_value" format="float" />
        <attr name="max_value" format="float" />
    </declare-styleable>
</resources>

Here is a couple of utility functions:

fun <T: Comparable<T>>T.fitInRange(range: Range<T>): T =
    when {
        this < range.lower -> range.lower
        this > range.upper -> range.upper
        else -> this
    }

fun Float.reduceToRange(rangeFrom: Range<Float>, rangeTo: Range<Float>): Float =
    when {
        this == rangeFrom.lower -> rangeTo.lower
        this == rangeFrom.upper -> rangeTo.upper
        else -> {
            val placeInRange = (this - rangeFrom.lower) / (rangeFrom.upper - rangeFrom.lower)
            ((rangeTo.upper - rangeTo.lower) * placeInRange) + rangeTo.lower
        }
    }

And at last, but not least - a class for vertical seek bar:

class ExpositionBar
@JvmOverloads
constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private val drawingRect = RectF(0f, 0f, 0f, 0f)
    private val drawingPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val strokeWidth: Float

    @ColorInt
    private val strokeColor: Int
    @ColorInt
    private val buttonFillColor: Int
    @ColorInt
    private val buttonFillColorPressed: Int

    private val icon: VectorDrawable

    private val valuesRange: Range<Float>

    private var centerX = 0f
    private var minY = 0f
    private var maxY = 0f

    private var buttonCenterY = 0f
    private var buttonRadiusExt = 0f
    private var buttonRadiusInt = 0f
    private var buttonMinY = 0f
    private var buttonMaxY = 0f
    private var buttonCenterBoundsRange = Range(0f, 0f)

    private var iconTranslationX = 0f
    private var iconTranslationY = 0f

    private var isInDragMode = false

    private var onValueChangeListener: ((Float) -> Unit)? = null

    private var oldOutputValue = Float.MIN_VALUE

    init {
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpositionBar)

        icon =  typedArray.getDrawable(R.styleable.ExpositionBar_button_icon) as VectorDrawable
        val iconSize = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_button_icon_size, 0)
        icon.setBounds(0, 0, iconSize, iconSize)

        strokeWidth = typedArray.getDimensionPixelSize(R.styleable.ExpositionBar_stroke_width, 0).toFloat()
        drawingPaint.strokeWidth = strokeWidth

        strokeColor = typedArray.getColor(R.styleable.ExpositionBar_stroke_color, Color.WHITE)
        buttonFillColor = typedArray.getColor(R.styleable.ExpositionBar_button_color, Color.BLACK)
        buttonFillColorPressed = typedArray.getColor(R.styleable.ExpositionBar_button_color_pressed, Color.BLUE)

        val minValue = typedArray.getFloat(R.styleable.ExpositionBar_min_value, 0f)
        val maxValue = typedArray.getFloat(R.styleable.ExpositionBar_max_value, 0f)
        valuesRange = Range(minValue, maxValue)

        typedArray.recycle()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        drawingRect.right = width.toFloat()
        drawingRect.bottom = height.toFloat()

        buttonCenterY = drawingRect.centerY()

        recalculateDrawingValues()
    }

    override fun onDraw(canvas: Canvas) {
        drawingPaint.color = strokeColor
        drawingPaint.style = Paint.Style.STROKE

        // Draw the center line
        canvas.drawLine(centerX, minY, centerX, buttonMinY, drawingPaint)
        canvas.drawLine(centerX, buttonMaxY, centerX, maxY, drawingPaint)

        // Draw the button
        canvas.drawCircle(centerX, buttonCenterY, buttonRadiusExt, drawingPaint)
        drawingPaint.style = Paint.Style.FILL
        drawingPaint.color = if(isInDragMode) buttonFillColorPressed else buttonFillColor
        canvas.drawCircle(centerX, buttonCenterY, buttonRadiusInt, drawingPaint)

        // Draw button icon
        canvas.translate(iconTranslationX, iconTranslationY)
        icon.draw(canvas)
        canvas.translate(-iconTranslationX, -iconTranslationY)
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if(!isEnabled) {
            return false
        }

        when(event.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                if(isButtonHit(event.y)){
                    isInDragMode = true
                    invalidate()
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if(isInDragMode) {
                    buttonCenterY = event.y.fitInRange(buttonCenterBoundsRange)
                    recalculateDrawingValues()
                    invalidate()

                    val outputValue = buttonCenterY.reduceToRange(buttonCenterBoundsRange, valuesRange)
                    if (outputValue != oldOutputValue) {
                        onValueChangeListener?.invoke(outputValue)
                        oldOutputValue = outputValue
                    }
                }
            }
            MotionEvent.ACTION_UP,
            MotionEvent.ACTION_CANCEL -> {
                isInDragMode = false
                invalidate()
            }
        }
        return true
    }

    fun setOnValueChangeListener(listener: ((Float) -> Unit)?) {
        onValueChangeListener = listener
    }

    private fun recalculateDrawingValues() {
        centerX = drawingRect.left + drawingRect.width()/2
        minY = drawingRect.top
        maxY = drawingRect.bottom

        buttonRadiusExt = drawingRect.width() / 2 - strokeWidth / 2
        buttonRadiusInt = buttonRadiusExt - strokeWidth / 2
        buttonMinY = buttonCenterY - buttonRadiusExt
        buttonMaxY = buttonCenterY + buttonRadiusExt

        val buttonCenterMinY = minY + buttonRadiusExt + strokeWidth / 2
        val buttonCenterMaxY = maxY - buttonRadiusExt - strokeWidth / 2
        buttonCenterBoundsRange = Range(buttonCenterMinY, buttonCenterMaxY)

        iconTranslationX = centerX - icon.bounds.width() / 2
        iconTranslationY = buttonCenterY - icon.bounds.height() / 2
    }

    private fun isButtonHit(y: Float): Boolean {
        return y >= buttonMinY && y <= buttonMaxY
    }
}

You can use it as shown here:

<com.shevelev.wizard_camera.main_activity.view.widgets.ExpositionBar
    android:id="@+id/expositionBar"
    android:layout_width="@dimen/mainButtonSize"
    android:layout_height="300dp"
    android:layout_gravity="end|center_vertical"

    android:layout_marginEnd="@dimen/marginNormal"
    android:layout_marginBottom="26dp"

    app:button_icon = "@drawable/ic_brightness"
    app:button_icon_size = "@dimen/toolButtonIconSize"
    app:stroke_width = "@dimen/strokeWidthNormal"
    app:stroke_color = "@color/mainButtonsForeground"
    app:button_color = "@color/mainButtonsBackground"
    app:button_color_pressed = "@color/mainButtonsBackgroundPressed"
    app:min_value="-100"
    app:max_value="100"
/>

Voila!

清欢 2024-09-18 05:54:58

隐藏垂直SeekBar

更简单的方法,你可以像这样制作一个SeekBar。

就像在视频播放器中增大或减小音量的方式一样。全部
可以在SeekBar 中操作三个Last 属性。

<LinearLayout
        android:layout_width="@dimen/_40dp"
        android:layout_height="wrap_content"
        android:layout_marginVertical="100dp"
        android:gravity="center"
        android:orientation="vertical"
        >
        <SeekBar
            android:layout_width="500dp"
            android:layout_height="300dp"
            android:layout_gravity="center"
            android:rotation="270"
            android:secondaryProgress="6"
            android:progress="15"
            android:progressDrawable="@null"
            android:thumbTint="@null"
            android:secondaryProgressTint="@null"
            />
    </LinearLayout>

Hidden Vertical SeekBar

In a simpler way, you can make a SeekBar like this.

Like the way to increase or decrease the volume in video players. All
three Last attributes can be manipulated in the SeekBar.

<LinearLayout
        android:layout_width="@dimen/_40dp"
        android:layout_height="wrap_content"
        android:layout_marginVertical="100dp"
        android:gravity="center"
        android:orientation="vertical"
        >
        <SeekBar
            android:layout_width="500dp"
            android:layout_height="300dp"
            android:layout_gravity="center"
            android:rotation="270"
            android:secondaryProgress="6"
            android:progress="15"
            android:progressDrawable="@null"
            android:thumbTint="@null"
            android:secondaryProgressTint="@null"
            />
    </LinearLayout>
纵山崖 2024-09-18 05:54:58

我几年前制作了垂直搜索栏库来解决这个问题,请参阅:

https://github.com/ASE55471/Android-VSlider

该库不会旋转原始搜索栏90或270度,它从头开始重写,以解决“拇指不在正确的位置”,“无法从代码设置位置”,“无法排列背景可绘制”,“触摸位置跑掉”等。有一些额外有用的功能。

I made vertical seekbar library years ago for solve this see:

https://github.com/ASE55471/Android-VSlider

This library does not rotate original seekbar 90 or 270 degree, It's rewrite from scratch for resolve "Thumb not in the right place", "Can't set position from code", "Can't line up background drawable", "Touch position run away" etc. And also have some extra useful feature.

墨落成白 2024-09-18 05:54:58

简单的答案

不要在搜索栏内使用 android:rotation="270" ,而是在环绕它的 FrameLayout 内使用它。

 <FrameLayout
    android:background="@color/gray"
    android:layout_width="300dp"
    android:layout_height="5dp"
    android:layout_marginEnd="-126dp"
    android:rotation="270"
    android:orientation="vertical"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent">
<SeekBar
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
</FrameLayout>

为了使我的框架布局的边距为 24dp,我计算了宽度 -150dp + 24dp,因为框架布局首先水平绘制,然后垂直旋转。

Simple answer

Instead of using android:rotation="270" inside of a seek bar, use it inside of a FrameLayout that wraps around it.

 <FrameLayout
    android:background="@color/gray"
    android:layout_width="300dp"
    android:layout_height="5dp"
    android:layout_marginEnd="-126dp"
    android:rotation="270"
    android:orientation="vertical"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent">
<SeekBar
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />
</FrameLayout>

To get my frame layout to be 24dp margin right I calculated width -150dp + 24dp because the frame layout is first drawn horizontally and then rotated vertically.

年华零落成诗 2024-09-18 05:54:57
  1. 对于API 11及更高版本,可以使用seekbar的XML属性(android:rotation="270")来实现垂直效果。

    
    
  2. 对于较旧的 API 级别(例如 API10),仅使用 Selva 的答案:
    https://github.com/AndroSelva/Vertical-SeekBar-Android

  1. For API 11 and later, can use seekbar's XML attributes(android:rotation="270") for vertical effect.

    <SeekBar
    android:id="@+id/seekBar1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:rotation="270"/>
    
  2. For older API level (ex API10), only use Selva's answer:
    https://github.com/AndroSelva/Vertical-SeekBar-Android

若言繁花未落 2024-09-18 05:54:57

这是垂直搜索栏的一个非常好的实现。
看看吧。

http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip

这是我自己的垂直和反向搜索栏的实现,基于 this

https://github.com/AndroSelva/Vertical-SeekBar-Android

protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(),0);

    super.onDraw(c);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_UP:
            int i=0;
            i=getMax() - (int) ((getMax()-getMin()) * event.getY() / getHeight());
            setProgress(i);
            Log.i("Progress",getProgress()+"");
            onSizeChanged(getWidth(), getHeight(), 0, 0);
            break;

        case MotionEvent.ACTION_CANCEL:
            break;
    }
    return true;
}

Here is a very good implementation of vertical seekbar.
Have a look.

http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip

And Here is my own implementation for Vertical and Inverted Seekbar based on this

https://github.com/AndroSelva/Vertical-SeekBar-Android

protected void onDraw(Canvas c) {
    c.rotate(-90);
    c.translate(-getHeight(),0);

    super.onDraw(c);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
        case MotionEvent.ACTION_UP:
            int i=0;
            i=getMax() - (int) ((getMax()-getMin()) * event.getY() / getHeight());
            setProgress(i);
            Log.i("Progress",getProgress()+"");
            onSizeChanged(getWidth(), getHeight(), 0, 0);
            break;

        case MotionEvent.ACTION_CANCEL:
            break;
    }
    return true;
}
我很OK 2024-09-18 05:54:57

工作示例

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

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

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

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

   @Override
   public synchronized void setProgress(int progress)  // it is necessary for calling setProgress on click of a button
   {
    super.setProgress(progress);
    onSizeChanged(getWidth(), getHeight(), 0, 0); 
   }
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }
}

粘贴代码并保存。现在在您的 XML 布局中使用它:

<android.widget.VerticalSeekBar
  android:id="@+id/seekBar1"
  android:layout_width="wrap_content"
  android:layout_height="200dp"
  />

确保创建一个包 android.widget 并在此包下创建 VerticalSeekBar.java

Working example

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;

public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

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

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

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

   @Override
   public synchronized void setProgress(int progress)  // it is necessary for calling setProgress on click of a button
   {
    super.setProgress(progress);
    onSizeChanged(getWidth(), getHeight(), 0, 0); 
   }
    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
        c.rotate(-90);
        c.translate(-getHeight(), 0);

        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                setProgress(getMax() - (int) (getMax() * event.getY() / getHeight()));
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }
}

There, paste the code and save it. Now use it in your XML layout:

<android.widget.VerticalSeekBar
  android:id="@+id/seekBar1"
  android:layout_width="wrap_content"
  android:layout_height="200dp"
  />

Make sure to create a package android.widget and create VerticalSeekBar.java under this package

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文