天天看點

【Android UI】自定義圓形SeekBar和自定義Dialog的結合使用

最近要做一個比較有趣的效果,就是将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的結合使用來實作自動關閉的效果。

運作效果如下:

【Android UI】自定義圓形SeekBar和自定義Dialog的結合使用

自定義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