Как сделать вертикальный SeekBar в Android?
Может ли панель поиска быть вертикальной? Я не очень хорошо разбираюсь в дизайне пользовательского интерфейса, поэтому, как сделать seekbar красивее, пожалуйста, дайте мне несколько шаблонов и примеров.
19 ответов
Вот очень хорошая реализация вертикальной панели поиска. Посмотри.
http://560b.sakura.ne.jp/android/VerticalSlidebarExample.zip
А вот моя собственная реализация для вертикального и перевернутого Seekbar на основе этого
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;
}
Для API 11 и более поздних версий можно использовать XML-атрибуты seekbar (android: вращение ="270") для вертикального эффекта.
<SeekBar android:id="@+id/seekBar1" android:layout_width="match_parent" android:layout_height="wrap_content" android:rotation="270"/>
Для более старого уровня API (например, API10) используйте только ответ Selva:
https://github.com/AndroSelva/Vertical-SeekBar-Android
Рабочий пример
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
под этим пакетом
Пытаться:
<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>
Мы сделали вертикальный SeekBar, используя 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>
Снимок экрана для компенсации экспозиции камеры:
Я использовал решение Selva, но у меня было два вида проблем:
- OnSeekbarChangeListener не работает должным образом
- Установка прогресса программно не работала должным образом.
Я исправил эти две проблемы. Вы можете найти решение (в рамках моего собственного пакета проекта) на
Это сработало для меня, просто поместите его в любой макет, который вы хотите.
<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>
Оберните его внутри 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>
Обратите внимание, мне кажется, что если вы измените ширину, ширина большого пальца не изменится правильно. Я не потратил время, чтобы все исправить, я просто исправил это для своего случая. Вот что я сделал. Не могу понять, как связаться с оригинальным создателем.
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();
}
Попробуй это
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);
}
}
При перемещении большого пальца с помощью EditText, setProgress вертикальной панели поиска может не работать. Следующий код может помочь:
@Override
public synchronized void setProgress(int progress) {
super.setProgress(progress);
updateThumb();
}
private void updateThumb() {
onSizeChanged(getWidth(), getHeight(), 0, 0);
}
Этот фрагмент кода находится здесь: /questions/25221397/vertikalnyij-seekbar-setprogress-ne-rabotaet/25221414#25221414
Вы можете сделать это самостоятельно - это сейчас так сложно. Вот пример из моего проекта: 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"
/>
Вуаля!
Используя
RotateLayout
, имея вертикальный ветерок. Просто заверните это ужасное
SeekBar
в него, а Боб - твой дядя:
<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>
Начиная
Добавьте эти строки в 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:splitTrack="false"
требуется для Android N+.
Я пробовал разными способами, но тот, который у меня сработал, оказался. Используйте Seekbar внутри 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"
/>
И в Code.
Настройка обратного вызова Pre Draw на панели поиска, где вы можете изменить ширину и высоту панели поиска. Я сделал эту часть на 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 Event, и когда он запускается, я удаляю PreDraw, чтобы он не переходил в бесконечный цикл.
Поэтому, когда выполняется предварительная отрисовка, я получаю высоту FrameLayout и назначаю ее панели поиска. И установите вращение панели поиска на 270. Так как моя панель поиска находится внутри Layout фрейма и ее Gravity установлен как Center. Мне не нужно беспокоиться о Переводе. Поскольку панель поиска всегда остается в середине макета кадра.
Причина, по которой я удаляю EventHandler, заключается в том, что seekbar.RequestLayout(); Заставит это событие снова выполнить.
Простой ответ
Вместо использования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>
Чтобы макет моего фрейма был правильным, я рассчитал ширину -150dp + 24 dp, потому что макет фрейма сначала рисуется горизонтально, а затем поворачивается по вертикали.
Несколько лет назад я создал библиотеку вертикальной панели поиска, чтобы решить эту проблему:
Эта библиотека не поворачивает исходную панель поиска на 90 или 270 градусов. Она переписана с нуля для устранения проблемы «Большой палец не в нужном месте», «Невозможно установить положение из кода», «Невозможно выровнять фон для рисования», «Положение касания». убежать» и т. д. А также иметь дополнительную полезную функцию.
В моем случае я использовал обычный seekBar и просто перевернул макет.
seekbark_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 вот так.
Нравится способ увеличения или уменьшения громкости в видеоплеерах. Все три атрибута Last можно манипулировать в 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>