How to make a vertical SeekBar in Android?

≯℡__Kan透↙ 提交于 2019-11-26 05:25:10

问题


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


回答1:


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() * event.getY() / getHeight());
            setProgress(i);
            Log.i("Progress",getProgress()+"");
            onSizeChanged(getWidth(), getHeight(), 0, 0);
            break;

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



回答2:


  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




回答3:


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




回答4:


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> 



回答5:


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




回答6:


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:




回答7:


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>



回答8:


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



回答9:


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




回答10:


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+.




回答11:


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



回答12:


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>



回答13:


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:




回答14:


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.



来源:https://stackoverflow.com/questions/3333658/how-to-make-a-vertical-seekbar-in-android

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!