最近要做一个比较有趣的效果,就是将android手机设置的条形的SeekBar换成圆形的SeekBar,这里我不讲怎么将定制系统的设置rom,但如果有人想了解设置里面的显示可以看看这篇博客,只讲怎么自定义圆形SeekBar和自定义Dialog,虽然网上有很多这方面的资料,但不系统,所以在这里总结下,方面以后观看。
自定义圆形SeekBar
关于圆形SeekBar我是在GitHub上下载的一个开源项目,项目地址,里面有详细的使用说明和参数含义。在这里,我也贴一下代码和使用步骤。
首先,我们从网站下载代码,看看解压包里面有CircularSeekBar.java和attrs.xml文件,一眼看下去就知道attrs.xml是属性的定义,CircularSeekBar.java是自定义圆形SeekBar的实现。下面贴一下attrs.xml看下有那些属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CircularSeekBar">
<attr name="progress" format="integer"></attr>
<attr name="max" format="integer"></attr>
<attr name="move_outside_circle" format="boolean"></attr>
<attr name="maintain_equal_circle" format="boolean"></attr>
<attr name="use_custom_radii" format="boolean"></attr>
<attr name="lock_enabled" format="boolean"></attr>
<attr name="circle_x_radius" format="dimension" />
<attr name="circle_y_radius" format="dimension" />
<attr name="circle_stroke_width" format="dimension" />
<attr name="pointer_radius" format="dimension" />
<attr name="pointer_halo_width" format="dimension" />
<attr name="pointer_halo_border_width" format="dimension"></attr>
<attr name="circle_color" format="color"></attr>
<attr name="circle_progress_color" format="color"></attr>
<attr name="pointer_color" format="color"></attr>
<attr name="pointer_halo_color" format="color"></attr>
<attr name="pointer_halo_color_ontouch" format="color"></attr>
<attr name="pointer_alpha_ontouch" format="integer"></attr>
<attr name="start_angle" format="float"></attr>
<attr name="end_angle" format="float"></attr>
<attr name="circle_fill" format="color"></attr>
</declare-styleable>
</resources>
要了解具体的属性看了,可以去GitHub上看下,这里就不累叙了。
之后,我们将自定义的CircularSeekBar.java导入我们的项目,当然attrs.xml也要导入,至于怎么自定义的CircularSeekBar大家可以自己看看源码。
最后,我们来写一个对这个CircularSeekBar的测试。先来看看UI的代码:
<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.yzx.circularseekbar.MainActivity" >
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="click"
android:layout_centerHorizontal="true"
android:text="弹出对话框"/>
</RelativeLayout>
这段代码没什好讲的,我们再来看看MainActivity的代码:
public class MainActivity extends Activity{
private SharedPreferences sp;
private AlertDialog dialog;
private Timer mTimer;
private TimerTask mTimerTask;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
dialog.dismiss();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
//Toast.makeText(this, "haha", Toast.LENGTH_LONG).show();
switch (v.getId()) {
case R.id.btn1:
sp = getSharedPreferences("brightness", MODE_PRIVATE);
showBrightnessDialog();
break;
default:
break;
}
}
private void showBrightnessDialog() {
//使用Timer来实现3秒自动关闭dialog
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
AlertDialog.Builder builder = new Builder(this);
View contentView = View.inflate(this, R.layout.brightness_dialog, null);
CircularSeekBar seekbar = (CircularSeekBar) contentView.findViewById(R.id.circularSeekBar1);
int brightnessValue = sp.getInt("brightnessValue", 45);
seekbar.setProgress(brightnessValue);
//注册监听事件
seekbar.setOnSeekBarChangeListener(new OnCircularSeekBarChangeListener() {
@Override
public void onProgressChanged(CircularSeekBar circularSeekBar,
int progress, boolean fromUser) {
//在此处修改亮度
System.out.println("circularSeekBar:"+progress);
}
@Override
public void onStopTrackingTouch(CircularSeekBar seekBar) {
// TODO Auto-generated method stub
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
Editor editor = sp.edit();
editor.putInt("brightnessValue", seekBar.getProgress());
editor.commit();
}
@Override
public void onStartTrackingTouch(CircularSeekBar seekBar) {
// TODO Auto-generated method stub
mTimer.cancel();
}
});
dialog = builder.create();
dialog.setView(contentView,0,0,0,0);
dialog.show();
//Handle实现过3秒自动关闭dialog。自动关闭对话框的功能主要使用Handler对象来实现,该对象的postDelayed方法用来实现延时多少秒去执行某个任务。
//有Bug,无论是否操作,Dialog过三秒都会关闭
/*Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
dialog.dismiss();
}
}, 3000);*/
}
}
我们先贴出brightness_dialog.xml的代码,再来分析下代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res/com.yzx.circularseekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="#000000"
tools:context="com.yzx.circularseekbar.MainActivity" >
<TextView
android:id="@+id/tv_brightness"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="20sp"
android:textColor="#0174DF"
android:text="亮度"/>
<com.yzx.circularseekbar.CircularSeekBar
android:id="@+id/circularSeekBar1"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="@id/tv_brightness"
app:circle_x_radius="100dp"
app:circle_y_radius="100dp"
app:use_custom_radii="true"
app:progress="25"
app:max="100"
app:pointer_alpha_ontouch="100"
app:pointer_color="#0174DF"
app:pointer_halo_color="#880174DF" />
</RelativeLayout>
上面代码达到的效果是,当你点击“弹出对话框”按钮时,弹出圆形SeekBar。如果你不动时,弹出的AlertDialog过三秒就会自动消失,当你拖动圆形SeekBar时,就不会消失,不拖动,过3秒又自动消失。这里面也记录的你的进度条拖动到什么位置了。
要注意,在brightness_dialog.xml中不要忘了一行代码:
xmlns:app="http://schemas.android.com/apk/res/com.yzx.circularseekbar"
实现自动关闭时通过使用Timer来实现的,本来想使用Handle里面的postDelayed来实现,但有一个不管你操不操作,过3秒就会关闭的Bug。Timer的主体代码如下:
//使用Timer来实现3秒自动关闭dialog
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
通过在单击“弹出对话框”按钮、onStartTrackingTouch和onStopTrackingTouch的结合使用来实现自动关闭的效果。
运行效果如下:
自定义Dialog
在上面的AlertDialog中,有一个隐藏的Bug,当我们单击按钮让弹窗弹出时马上关闭,之后又点击按钮让弹窗弹出,循环几次,就会发现弹窗不可控的情况出现,其根本原因就是我们开启了多个Timer,而在关闭(dismiss)时没有终止Timer造成的。
对与以上的Bug,我们需要自定义Dialog,重写自定义的Dialog中的dismiss方法,让其在每次退出时就终止Timer。
我们现在主UI中添加一个按钮,来调用自定义的Dialog。添加后的完整代码如下:
<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.yzx.circularseekbar.MainActivity" >
<Button
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="click"
android:layout_centerHorizontal="true"
android:text="弹出对话框"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btn1"
android:onClick="click"
android:layout_centerInParent="true"
android:text="弹出对话框_自定义" />
</RelativeLayout>
brightness_dialog.xml中的代码不变,而MainActivity的完整代码如下:
public class MainActivity extends Activity{
private SharedPreferences sp;
private AlertDialog dialog;
private MyAlertDialog myDialog;
private Timer mTimer;
private TimerTask mTimerTask;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
dialog.dismiss();
break;
case 2:
myDialog.dismiss();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click(View v){
//Toast.makeText(this, "haha", Toast.LENGTH_LONG).show();
switch (v.getId()) {
case R.id.btn1:
sp = getSharedPreferences("brightness", MODE_PRIVATE);
showBrightnessDialog();
break;
case R.id.btn2:
sp = getSharedPreferences("brightness", MODE_PRIVATE);
showMyBrightnessDialog();
break;
default:
break;
}
}
private void showMyBrightnessDialog() {
myDialog = new MyAlertDialog(MainActivity.this);
//设置为没有title
myDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
myDialog.show();
//使用Timer来实现3秒自动关闭dialog
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 2;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
}
//重写Dialog,实现每次dismiss时终止定时器
class MyAlertDialog extends Dialog{
protected MyAlertDialog(Context context) {
super(context);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.brightness_dialog);
CircularSeekBar seekbar = (CircularSeekBar) findViewById(R.id.circularSeekBar1);
int brightnessValue = sp.getInt("customBrightnessValue", 45);
seekbar.setProgress(brightnessValue);
//注册监听事件
seekbar.setOnSeekBarChangeListener(new OnCircularSeekBarChangeListener() {
@Override
public void onProgressChanged(CircularSeekBar circularSeekBar,
int progress, boolean fromUser) {
//在此处修改亮度
System.out.println("circularSeekBar's progress is :"+progress);
}
@Override
public void onStopTrackingTouch(CircularSeekBar seekBar) {
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 2;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
Editor editor = sp.edit();
editor.putInt("customBrightnessValue", seekBar.getProgress());
editor.commit();
}
@Override
public void onStartTrackingTouch(CircularSeekBar seekBar) {
mTimer.cancel();
}
});
}
@Override
public void dismiss() {
// 终止所有的定时器
mTimer.cancel();
System.out.println("circularSeekBar is auto close.");
super.dismiss();
}
}
private void showBrightnessDialog() {
//使用Timer来实现3秒自动关闭dialog
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
AlertDialog.Builder builder = new Builder(this);
View contentView = View.inflate(this, R.layout.brightness_dialog, null);
CircularSeekBar seekbar = (CircularSeekBar) contentView.findViewById(R.id.circularSeekBar1);
int brightnessValue = sp.getInt("brightnessValue", 45);
seekbar.setProgress(brightnessValue);
//注册监听事件
seekbar.setOnSeekBarChangeListener(new OnCircularSeekBarChangeListener() {
@Override
public void onProgressChanged(CircularSeekBar circularSeekBar,
int progress, boolean fromUser) {
//在此处修改亮度
System.out.println("circularSeekBar:"+progress);
}
@Override
public void onStopTrackingTouch(CircularSeekBar seekBar) {
// TODO Auto-generated method stub
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
//开始一个定时任务
mTimer.schedule(mTimerTask, 3000);
Editor editor = sp.edit();
editor.putInt("brightnessValue", seekBar.getProgress());
editor.commit();
}
@Override
public void onStartTrackingTouch(CircularSeekBar seekBar) {
// TODO Auto-generated method stub
mTimer.cancel();
}
});
dialog = builder.create();
dialog.setView(contentView,0,0,0,0);
dialog.show();
//Handle实现过3秒自动关闭dialog。自动关闭对话框的功能主要使用Handler对象来实现,该对象的postDelayed方法用来实现延时多少秒去执行某个任务。
//有Bug,无论是否操作,Dialog过三秒都会关闭
/*Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
dialog.dismiss();
}
}, 3000);*/
}
}
上面的代码主要的部分就是:
@Override
public void dismiss() {
// 终止所有的定时器
mTimer.cancel();
System.out.println("circularSeekBar is auto close.");
super.dismiss();
}
就是每次调用dismiss时就终止Timer,这样就解决的弹窗的不可控。
源码下载地址:https://github.com/yzhixiang/CircularSeekBar-CustomDialog