Як зробити вертикальну панель пошуку в Android?


75

Чи може SeekBarбути вертикальним? Я не дуже добре володію дизайном інтерфейсу, тож як я можу зробити його SeekBarкрасивішим, дайте мені кілька шаблонів та прикладів.


2
Ви повинні скотити власну реалізацію. Незавершеною версія пов'язана тут і ще тут
Pentium10

Відповіді:


51

Ось дуже хороша реалізація вертикальної панелі пошуку. Гляньте.

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

І ось моя власна реалізація для вертикальної та інвертованої панелі пошуку на основі цього

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
привіт там. Я спробував код, який ви опублікували як .zip, і він чудово працює .. за винятком однієї проблеми: SeekBar все ще малює свій прогрес зліва направо, а не знизу вгору. Не знайшов, де він закодований. У вас є пропозиції щодо мене? thx
Droidman

2
Привіт! Полюбіть свою вертикальну смужку прокрутки Перевернута, я намагався створити таку, але мав багато труднощів із пошуком правильного обертання та перекладу. Я все-таки знайшов одну невеличку помилку: якщо MAX не 100, прогрес розраховується неправильно. Щоб виправити це, потрібно змінити (100-i) на (getMax () - i). Таким чином, він ідеально підходить для будь-якого асортименту.
руппс

мабуть, тут я можу бути і негативним, і більшим за макс, тому я змінив його наMath.max(0, Math.min(getMax(), i)
Маттіас

1
@AndroSelva searchbar.setprogress (position) не переміщує вертикальний великий палець панелі пошуку у потрібне положення. ....чому так ?
Анімеш Мангла

THUMB НЕ ПРАЦЮЄ
25

75
  1. Для API 11 та пізніших версій можна використовувати атрибути XML пошукової панелі (android: rotation = "270") для вертикального ефекту.

    <SeekBar
    android:id="@+id/seekBar1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:rotation="270"/>
    
  2. Для попереднього рівня API (наприклад, API10) використовуйте лише відповідь Selva:
    https://github.com/AndroSelva/Vertical-SeekBar-Android


будь ласка, надайте посилання на відповідь Сельви.
Nikhil Agrawal

@ Nikhil , Відповідь Сельви - це відповідь, прийнята автором вище. Я думаю, що власний інвентар Selva простий і хороший!
Сатурн

19
Рішення 1 - повернена панель пошуку не працює належним чином. Великий палець відтягується, розмір і положення неможливо встановити належним чином (принаймні всередині RelativeLayout).
Нуль покажчика

2
Це працює, однак, ви повинні пам’ятати, що розміри міняються місцями, тому те, що раніше було шириною, стає висотою тощо. Отже, коли ви збільшуєте висоту, ви фактично її розтягуєте, саме тому великий палець відводиться, і це трапляється навіть із звичайною панеллю пошуку, якщо ви витягнули ширину. Поворот дратує, тому я думаю, що простіше розмістити панель пошуку всередині макета та правильно розмірити її, а потім повернути макет та змінити розмір відповідно до основного макета.
Фахад Алдурайбі

4
Ах, я занадто швидко проголосував за тебе. Це навряд чи працює. Повзунок внутрішньо використовує ширину / висоту перед обертанням, тому все незручно розташоване та розмірене.
b005t3r

25

Робочий приклад

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під цим пакетом


Працює ідеально! Дякую!
mjp66

це дає не великий палець, а лише повзунок
Fahid Nadeem

спробуйте встановити власний великий палець за допомогою android: thumb = "@ drawable / thumb_image". Але я не стикаюся з проблемою. stackoverflow.com/a/18559778/3496570
AndroidGeek

У мене також є проблема з великим пальцем з цим рішенням. Він відображається, але не анімує (його розмір не збільшується) під час переміщення, як це робить звичайний.
Тім Отін

@XarEAhmer, ти знаєш, як я можу підтримувати двонаправлені мови, як-от, Arabicколи це Vertical SeekBarперевернулося, а повзунок дає хибний напрямок прогресу?
blueware

15

Спробуйте:

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

@Mahmoud Додавши андроїд: rotacija = "270"
Алі АльНоаймі

6
Встановлення обертання повертає повзунок, але не макет. layout_width все ще контролює довжину повзунка. Як такі макети все ще вважають, що це ширина, а не висота. Це робить цікавим правильне вставлення в дизайн. Я досі не зрозумів, як поставити поворотну панель пошуку на GridLayout.
JT.

10

Я використовував рішення Selva, але мав два типи проблем:

  • OnSeekbarChangeListener не працював належним чином
  • Програмне встановлення прогресу не працювало належним чином.

Я виправив ці дві проблеми. Ви можете знайти рішення (в моєму власному пакеті проектів) за адресою

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


Чудово працює, дякую. Вам також слід додати нульову перевірку змінної mOnSeekBarChangeListener.
jekatt

@ JörgEisfeld встановлення великого пальця та прогресу Drawab le не вдався для мене
noobEinstien

@ JörgEisfeld, ти знаєш, як я можу підтримувати двонаправлені мови, як-от, Arabicколи це Vertical SeekBarперевернулося, а повзунок дає хибний напрямок прогресу?
blueware

10

Ми зробили вертикальну панель пошуку за допомогою 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>

Знімок екрана для компенсації експозиції камери:

введіть тут опис зображення


1
дякую за вказівник! фокус полягає у встановленні висоти .... інакше framelayout використовує ширину шукача до обертання
ShellDude

Дякую. працював з найменшим кодом для введення.
federico verchez

9

Це спрацювало для мене, просто вкладіть його в будь-який потрібний макет.

<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 використовує ширину шукача до обертання
ShellDude

це та сама відповідь, яку раніше давав Максим Міхісор?
Federico Verchez

6

Зауважте, мені здається, що якщо ви змінюєте ширину, ширина великого пальця змінюється неправильно. Я не знайшов часу, щоб виправити це правильно, я просто виправив це для свого випадку. Ось що я зробив. Не вдалося з’ясувати, як зв’язатись із початковим автором.

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

5

Загорніть його в 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>

2

При переміщенні великого пальця за допомогою EditText, вертикальна панель пошуку 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


1

Починаємо

Додайте ці рядки до 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 +.


1

Спробуйте це

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

Цей підхід має недолік - повідомляється, progressможе становити менше, ніж мінімальне або максимальне значення, якщо користувач продовжує перетягувати великий палець за межі подання.
Роман Самойленко

1

Я намагався різними способами, але той, що працював у мене, був. Використовуйте панель пошуку всередині 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"
    />

І в Кодексі.

Налаштування попереднього виклику зворотного виклику на панелі пошуку, де ви можете змінити ширину та висоту панелі пошуку. Я зробив цю частину в 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, щоб він не потрапляв у нескінченний цикл.

Отже, коли попереднє малювання виконується, я отримую висоту FrameLayout і призначаю її панелі пошуку. І встановіть обертання панелі пошуку на 270. Оскільки моя панель пошуку знаходиться всередині макета кадру, а її Гравітація встановлена ​​як Центр. Мені не потрібно турбуватися про переклад. Оскільки Seekbar завжди залишається посередині Frame Layout.

Причина, по якій я вилучаю EventHandler, полягає у тому, що шукати.RequestLayout (); Зробить цю подію повторною.


1

Ви можете зробити це самостійно - це зараз так складно. Ось приклад з мого проекту: 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"
/>

Вуаля!


0

У моєму випадку я використовував звичайну панель пошуку і просто перевернув макет.

seebark_layout.xml - мій макет, що містить панель пошуку, яку нам потрібно зробити вертикальною.

<?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 я обертаю searchbar_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()
    }
}
}

В результаті ми маємо необхідну вертикальну панель пошуку: введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.