Tạo SeekBar dọc (Vertical SeekBar) trong Android
Hướng dẫn này minh họa cách tạo SeekBar dọc (vertical) bằng cách xoay SeekBar ngang 270 độ.
Bước 1: Tạo dự án mới
Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.
Bước 2: Thiết kế giao diện (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:rotation="270"/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:text=""
android:textSize="16sp" />
</RelativeLayout>
Bước 3: Code MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
SeekBar seekBar;
TextView textView;
int min = 0, max = 100, current = 50;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
seekBar = findViewById(R.id.seekBar);
// Thiết lập phạm vi và giá trị hiện tại
seekBar.setMax(max - min); // Max = 100
seekBar.setProgress(current - min); // Progress = 50
textView.setText(String.valueOf(current));
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
current = progress + min;
textView.setText(String.valueOf(current));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Bắt đầu kéo
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Kết thúc kéo
}
});
}
}
Bước 4: Cấu hình AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="app.com.sample">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Chạy ứng dụng
Kết nối thiết bị, chạy app. SeekBar hiển thị dọc giữa màn hình. Kéo lên/xuống để thay đổi giá trị (0-100), TextView trên cùng cập nhật real-time.
⚠️ Vấn đề với rotation="270":
- Touch area có thể không khớp hiển thị (vùng chạm vẫn theo orientation gốc)
- Layout params width/height bị đảo ngược logic
- Trên một số thiết bị/API, rotation không hoạt động tốt
Cách tốt hơn: Custom VerticalSeekBar
Tạo class VerticalSeekBar.java:
public class VerticalSeekBar extends AppCompatSeekBar {
public VerticalSeekBar(Context context) {
super(context);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(h, w, oldh, oldw); // Swap width/height
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec); // Swap
setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth()); // Swap
}
@Override
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;
// Invert Y coordinate
float y = getHeight() - event.getY();
float x = event.getX();
MotionEvent invertedEvent = MotionEvent.obtain(
event.getDownTime(), event.getEventTime(),
event.getAction(), x, y,
event.getMetaState()
);
boolean result = super.onTouchEvent(invertedEvent);
invertedEvent.recycle();
return result;
}
}
Sử dụng trong XML:
<com.example.VerticalSeekBar
android:id="@+id/verticalSeekBar"
android:layout_width="wrap_content"
android:layout_height="200dp"
android:max="100"
android:progress="50"
android:thumb="@drawable/custom_thumb"
android:progressDrawable="@drawable/custom_progress" />
SeekBar tùy chỉnh giao diện (Custom Thumb & Progress)
res/drawable/custom_thumb.xml:
<shape xmlns:android="https://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="#FF5722"/>
<size android:width="24dp" android:height="24dp"/>
<stroke android:width="2dp" android:color="#FFFFFF"/>
</shape>
res/drawable/custom_progress.xml:
<layer-list xmlns:android="https://schemas.android.com/apk/res/android">
<!-- Background track -->
<item android:id="@android:id/background">
<shape>
<corners android:radius="4dp"/>
<solid android:color="#E0E0E0"/>
</shape>
</item>
<!-- Progress track -->
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="4dp"/>
<solid android:color="#FF5722"/>
</shape>
</clip>
</item>
</layer-list>