본문 바로가기

구글과인터넷/안드로이드

안드로이드 나만의 Seekbar 만들기

Seekbar는 Progressbar를 상속 받아서 구현되어있습니다.
Progressbar의 구성인 background, secondaryProgress, progress 이 세부분을 구성하는 xml을 만들어야 합니다. 
그리고 Progressbar와는 다르게 thumb라는 것이 있습니다. 이 것은 progress를 조절하는 바와 같은 것입니다. 

background, secondaryProgress, progress 구성을 위한 xml (progress_horizontal_custom.xml)

<?xml version="1.0" encoding="utf-8"?>

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />
            <gradient
    android:startColor="#ff9d9e9d"
    android:centerColor="#ff5a5d5a"
    android:centerY="0.75"
    android:endColor="#ff747674"
    android:angle="270" />
        </shape>
    </item>
    
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <corners android:radius="5dip" />
                <gradient
     android:startColor="#80ffd300"
     android:centerColor="#80ffb600"
     android:centerY="0.75"
     android:endColor="#a0ffcb00"
     android:angle="270" />
            </shape>
        </clip>
    </item>
    
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <corners android:radius="5dip" />
                <gradient
     android:startColor="#ff55d300"
     android:centerColor="#ff55b600"
     android:centerY="0.75"
     android:endColor="#ff11cb00"
     android:angle="270" />
            </shape>
        </clip>
    </item>
    
</layer-list>



thumb를 구성하는 xml (seek_thumb_custom.xml)
<?xml version="1.0" encoding="UTF-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">     
     <!--  Press the State  -->
        <item 
                android:state_focused="true" 
                android:state_pressed="true" 
                android:drawable="@drawable/circle_thumb" />     
        <!--  Ordinary focus state   -->
        <item 
                android:state_focused="false" 
                android:state_pressed="false"
                android:drawable="@drawable/circle_thumb" />           
        <!--  The State has the focus  -->
        <item 
                android:state_focused="true" 
                android:state_pressed="false"           
                android:drawable="@drawable/circle_thumb" />      
        <!--  Has focus   -->
        <item 
                android:state_focused="true"           
                android:drawable="@drawable/circle_thumb" />
        <item 
                android:state_focused="false" 
                android:state_pressed="true"
                android:drawable="@drawable/circle_thumb" />
              
</selector>


circle_thumb.xml
<!--
<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="ring"
  android:useLevel="false"  >
    <solid android:color="#FF000000"/> 
    <stroke android:width="4dp"
    android:color="#99FF0000"
    android:dashWidth="4dp"
    android:dashGap="2dp" /> 
   <size android:width="20dp"
    android:height="25dp"/>
    
</shape>
-->

<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="oval"> 
    <solid android:color="#FF000000"/> 
    
    <padding android:left="7dp"
    android:top="7dp"
    android:right="7dp"
    android:bottom="7dp" /> 
    <corners android:radius="4dp" /> 
    <gradient android:startColor="#FFFF0000"
    android:endColor="#80FF00FF"
    android:angle="270"/>    
   <size android:width="20dp"
    android:height="20dp"/>
</shape>

 <!--
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="line"> 
   <stroke
  android:width="1dp"
  android:color="#FF000000"
  android:dashWidth="1dp"
  android:dashGap="2dp" />   
   <size android:width="20dp"
    android:height="25dp"/>
</shape>
-->

<!-- 
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="rectangle">  
    <stroke
  android:width="2dip"
  android:color="#19CD00" />
    <corners
     android:bottomRightRadius="1dip"
     android:bottomLeftRadius="1dip"  
  android:topLeftRadius="1dip"
  android:topRightRadius="1dip"/>  
    <padding
  android:left="1dip"
  android:top="1dip"
  android:right="1dip"
  android:bottom="1dip" />
   <size android:width="20dp"
    android:height="25dp"/>
</shape>
-->


위의 xml들을 SeekBar에 설정하기 위해서는 xml에서 설정하는 방법과 자바코드에서 설정하는 방법 두가지가 있습니다. xml에서 설정하는 방법은...아래와 같습니다.
main.xml

...

    <SeekBar android:id="@+id/SeekBar1" 
        android:layout_width="300dp"
        android:layout_height="20dp"
        android:max="100"
        android:progress="0"
        android:secondaryProgress="0"
        android:progressDrawable="@drawable/progress_horizontal_custom" 
        android:thumb="@drawable/seek_thumb_custom" />
...


자바코드에서는 아래의 두 함수를 사용하면 되겠죠..?
SeekBarTest.java

setProgressDrawable(Drawable d);
setThumb(Drawable thumb); 

문제점이 있으면 댓글 달아주세요.

///////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////




Progress바와 유사하나, thumb를 옮겨 사용자가 원하는 위치로 이동하는 것이 가능하다.

1) 만드는 법
   - 
2) Seekbar 변경에 반응하기

3) style 바꾸는 법
   - thumb 바꾸는 법
     thumb를 바꾸는 법은 쉽다. 함수를 사용할 경우,
     mSeekbar.setThumb(myDrawable);
     layout을 사용할 경우 아래 속성을 Seekbar 태그에 포함시킨다.
     android:thumb="@drawable/pb_slider_thumb1"
   - thumb 없애기
     mSeekbar.setThumb(null);
     layout을 사용해서는 어떻게 해야 하는지 모르겠다.
   - background, foregound 바꾸는 법
     xml로 이루어진 drawable을 추가한다.
     (이 때, 이미지들이 9patch가 안되어 있다면 동일한 이미지가 반복적으로 나타난다.)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@android:id/background"
              android:drawable="@drawable/pb_slider_normal" />
        <item android:id="@android:id/secondaryProgress">
              <clip android:drawable="@drawable/pb_slider_normal"/>
        </item>
        <item android:id="@android:id/progress">
              <clip android:drawable="@drawable/pb_slider_highlight"/>
        </item>
</layer-list>
      seekbar 레이아웃에 이 xml 파일을 설정한다.
<SeekBar android:id="@+id/seek"
         android:layout_width="240dp"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="50"
        android:secondaryProgress="75" android:thumb="@drawable/pb_slider_thumb1"
        android:progressDrawable="@drawable/seekbar_drawable"/>




4) seekbar에 대한 이벤트 감지
        mSeekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                mplayer.seekTo(progress);
            }
       });

5) seekbar 위치 변경
       position / maxvalue 의 위치로 설정
       sk.setMax(maxvalue);
       sk.setProgress(position);

참고 자료
http://blog.naver.com/ween8/50082179209
http://www.codeweblog.com/android-modify-seekbar-style/
http://www.helloandroid.com/tutorials/musicdroid-audio-player-part-i


////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////


출처: http://bunhere.tistory.com/113




Progress바와 유사하나, thumb를 옮겨 사용자가 원하는 위치로 이동하는 것이 가능하다.

1) 만드는 법
   - 
2) Seekbar 변경에 반응하기

3) style 바꾸는 법
   - thumb 바꾸는 법
     thumb를 바꾸는 법은 쉽다. 함수를 사용할 경우,
     mSeekbar.setThumb(myDrawable);
     layout을 사용할 경우 아래 속성을 Seekbar 태그에 포함시킨다.
     android:thumb="@drawable/pb_slider_thumb1"
   - thumb 없애기
     mSeekbar.setThumb(null);
     layout을 사용해서는 어떻게 해야 하는지 모르겠다.
   - background, foregound 바꾸는 법
     xml로 이루어진 drawable을 추가한다.
     (이 때, 이미지들이 9patch가 안되어 있다면 동일한 이미지가 반복적으로 나타난다.)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@android:id/background"
              android:drawable="@drawable/pb_slider_normal" />
        <item android:id="@android:id/secondaryProgress">
              <clip android:drawable="@drawable/pb_slider_normal"/>
        </item>
        <item android:id="@android:id/progress">
              <clip android:drawable="@drawable/pb_slider_highlight"/>
        </item>
</layer-list>
      seekbar 레이아웃에 이 xml 파일을 설정한다.
<SeekBar android:id="@+id/seek"
         android:layout_width="240dp"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="50"
        android:secondaryProgress="75" android:thumb="@drawable/pb_slider_thumb1"
        android:progressDrawable="@drawable/seekbar_drawable"/>




4) seekbar에 대한 이벤트 감지
        mSeekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
            public void onStartTrackingTouch(SeekBar seekBar) {
            }
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
                mplayer.seekTo(progress);
            }
       });

5) seekbar 위치 변경
       position / maxvalue 의 위치로 설정
       sk.setMax(maxvalue);
       sk.setProgress(position);

참고 자료
http://blog.naver.com/ween8/50082179209
http://www.codeweblog.com/android-modify-seekbar-style/
http://www.helloandroid.com/tutorials/musicdroid-audio-player-part-i

/////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////


출처: http://vissel.tistory.com/136

오랜만에 정교한 설명 들어 갑니다!!

먼저 캡쳐된 이미지를 보시죠 


기본 셋팅입니다.,
먼저 레이아웃에 시크바를 정의 합니다 저기 maxHeight는 시크바의 높이의 최대값을 지정해줍니다. 썸네일은 씨크바가 이동하는 끝에 붙는 이미지 인데요 저 이미지를 써서 씨크바 버튼을 만들어 줄 수 있습니다. 





레이아웃에 지정된 시크바를 소스에서 위치랑 이벤트를 지정 해주어야 합니다. 

위치는 보시는거와 같이 소스에서 지정해 주실수도 있고 레이아웃에서도 간단하게 지정하실 수 있겠죠

 m_Sound_SeekBar = (SeekBar)findViewById(R.id.soundseek);
     m_Sound_SeekBar.setMax(100);
     m_Sound_SeekBar.setOnSeekBarChangeListener(soundcontrolListener);
     m_Sound_SeekBar.incrementProgressBy(1);

중요한 부분이 여기 인데요 

시크바를 정의 하고 
setMax를 통해서 시크바의 최대값을 설정해 줍니다 제가 100을 했으면 0부터 100까지의 값이 시크바에 설정 됩니다.
그리고 시키바의 리스너를 등록해주는데요 저것을 등록해주면 시크바의 움직임에 대해 이벤트를 줄 수 있습니다 밑에 캡쳐 영상 보시면 아시겠지만..
incrementPreogerssBy 는 시키바가 얼마큼씩 증가 하느냔대요 머 당연히 1씩 증가 시키게 했습니다. 






보시는 거와 같이 리스너를 등록해서 시크바의 움직임을 위치를 알아낼 수 있게 했습니다.  

이렇게 하고 컴파일 하면 !!!






간단하게 시크바를 완성했습니다 그러면 시키바를 움직였을 때 로그를 보시겠습니다. 값이 잘 얻어 오는지.. 







눌렀을 때 띄었을때 움직였을 때 시크바 버튼의 위치가 정확하게 나오네요 100까지 증가하 는 것도 확인되구요 캡쳐에선 안나오지만 ㅡㅡㅋㅋ 너무 길어져서;

아무튼 시크바는 이런 식으로 이용하시면 됩니다. 

이용방법은 만약에 시크바의 위치를 이벤트로 움직인다면 

.seekTo를 이용하시면 되구요 

미디어에 연결 하실때도 미디어의 크기 즉 getDuration을 쓰시면 최종 값의 크기를 맥스로 잡으시고 

실시간으로 값을 가져오는 getCurrentPosition를 seekTo로 잡으시면 미디어가 움직임에 따른 시크바를 구현 하실 수 있습니다.

더 추가해서 s시크바.setOnSeekBarChangeListener(this);를 쓰시고 public void onProgressChanged(SeekBar seekbar, int progress, boolean fromTouch) { 

이 함수를 쓰셔도 되구요 ㅎㅎ 이건 안드로이드 API보시면 쉽게 이해하실 거라 생각됩니다. 하지만 하나의 화면에 여러개의 시크바면 제방법을 쓰시는게 쉽습니다.

보나스..

시크바에 색을 넣고 싶거나 이미지 또는 그라데이션을 넣고 싶을 때

걍 간단하게 그라데이션 넣는 방법을 소개 합니다.

레이아웃에 

저 안에 
android:progressDrawable="@xml/progress_effectcolor"
지정 해주시고요 

xml을 만들어야죠.

그라데이션 지정 하는 방법은 

엑셈엘 파일 만드시고 

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />
            <gradient
                    android:startColor="#ff9d9e9d"
                    android:centerColor="#ff5a5d5a"
                    android:angle="270"
                            android:centerY="0.75"
                    android:endColor="#ff747674"             
            />
        </shape>
    </item> 
    <item android:id="@android:id/secondaryProgress">
        <clip>
            <shape>
                <corners  android:radius="5dip"/>
                <gradient
                        android:startColor="#80ffd300"
                        android:centerColor="#80ffb600"
                        android:angle="270"
                        android:centerY="0.75"
                        android:endColor="#a0ffcb00"               
                />
            </shape>
        </clip>
    </item> 
    <item android:id="@android:id/progress">
        <clip>
            <shape>
    <corners android:radius="5dip" />
            <gradient
                   android:startColor="#FA8072" 
            android:centerColor="#FF0000" 
            android:endColor="#DC143C" 
            android:angle="0" 

                        
            />

            </shape>
        </clip>
    </item> 
</layer-list>


추가해 주세요  보시면 백그라운드 색상과 세컨더리 이건 이동할 때 색인가; 암튼 마지막 프로그래스가 이동 하고 지나간 자리 색상을 추가하시면 됩니다... 시크바가 더욱 이쁘게 꾸미실 수 있습니다 컬러는 시작 중간 끝의 색을 지정 해주는데요 색상표는 게시판에 있는 

를 참고하셔서 하시면 됩니다.. 

어떻게 됬을까요...






점점 색이 밝아 지게 했습니다 더 이쁘게 꾸며도 되구요 ㅎㅎㅎ

오늘은 진짜 좀 자세히 했습니다 ㅋㅋㅋ 


//////////////////////////////////////////////////////////////////////////////////////////////////////


출처: http://ememomo.tistory.com/58

세로 seekbar 만들기 


가로로 커스터 마이징한 프로그래스바(SeekBar)에 대해서는 전 강좌에서 설명한 적이 있다.

가로에 대한 설명은 

http://ememomo.tistory.com/entry/커스톰RatingBarProgressBar

이부분을 참고하시기 바란다.

이번에 소개할 내용은 이 프로그래스바를 세로로 커스터 마이징 하는 것이다. 필자도 인터넷에 떠도는 소스를 가지고 튜닝해서
사용했다. 만드는 방법은 어렵지만 사용법은 간단하므로 따라해 보길 바란다. 

직접적으로 컨트롤할 부분은 사용할 Activity 내에서만 잘 정의해 주면 되므로 어려울 것이 없다. 

실행화면을 먼저 보여 주자면 








이런 모습으로 만들 수 있다. 

만든 모습을 보면 총 3개의 Java 파일을 이용한다. 먼저 View를 상속한 클래스 두개를 이용하여 만든다. 1. VerticalProgressBar.Java
  1. package com.ememomo.tistory;  
  2.   
  3. import com.tokaracamara.android.verticalslidevar.R;  
  4.   
  5. import android.content.Context;  
  6. import android.content.res.TypedArray;  
  7. import android.graphics.Bitmap;  
  8. import android.graphics.Canvas;  
  9. import android.graphics.Rect;  
  10. import android.graphics.drawable.BitmapDrawable;  
  11. import android.graphics.drawable.ClipDrawable;  
  12. import android.graphics.drawable.Drawable;  
  13. import android.graphics.drawable.LayerDrawable;  
  14. import android.graphics.drawable.ShapeDrawable;  
  15. import android.graphics.drawable.StateListDrawable;  
  16. import android.graphics.drawable.shapes.RoundRectShape;  
  17. import android.graphics.drawable.shapes.Shape;  
  18. import android.util.AttributeSet;  
  19. import android.util.Log;  
  20. import android.view.Gravity;  
  21. import android.view.View;  
  22. import android.view.ViewDebug;  
  23. import android.view.ViewParent;  
  24. import android.widget.RemoteViews.RemoteView;  
  25. import android.os.Parcel;  
  26. import android.os.Parcelable;  
  27.   
  28.   
  29.   
  30.   
  31. @RemoteView  
  32. public class VerticalProgressBar extends View {  
  33.     private static final int MAX_LEVEL = 10000;  
  34.   
  35.     int mMinWidth;  
  36.     int mMaxWidth;  
  37.     int mMinHeight;  
  38.     int mMaxHeight;  
  39.   
  40.     private int mProgress;  
  41.     private int mSecondaryProgress;  
  42.     private int mMax;  
  43.   
  44.     private Drawable mProgressDrawable;  
  45.     private Drawable mCurrentDrawable;  
  46.     Bitmap mSampleTile;  
  47.     private boolean mNoInvalidate;  
  48.     private RefreshProgressRunnable mRefreshProgressRunnable;  
  49.     private long mUiThreadId;  
  50.   
  51.     private boolean mInDrawing;  
  52.   
  53.     protected int mScrollX;  
  54.     protected int mScrollY;  
  55.     protected int mPaddingLeft;  
  56.     protected int mPaddingRight;  
  57.     protected int mPaddingTop;  
  58.     protected int mPaddingBottom;  
  59.     protected ViewParent mParent;  
  60.   
  61.     /** 
  62.      * Create a new progress bar with range 0...100 and initial progress of 0. 
  63.      * @param context the application environment 
  64.      */  
  65.     public VerticalProgressBar(Context context) {  
  66.         this(context, null);  
  67.     }  
  68.   
  69.     public VerticalProgressBar(Context context, AttributeSet attrs) {  
  70.         this(context, attrs, android.R.attr.progressBarStyle);  
  71.     }  
  72.   
  73.     public VerticalProgressBar(Context context, AttributeSet attrs, int defStyle) {  
  74.         super(context, attrs, defStyle);  
  75.         mUiThreadId = Thread.currentThread().getId();  
  76.         initProgressBar();  
  77.   
  78.         TypedArray a =  
  79.             context.obtainStyledAttributes(attrs, R.styleable.ProgressBar, defStyle, 0);  
  80.   
  81.         mNoInvalidate = true;  
  82.   
  83.         Drawable drawable = a.getDrawable(R.styleable.ProgressBar_android_progressDrawable);  
  84.         if (drawable != null) {  
  85.             drawable = tileify(drawable, false);  
  86.             // Calling this method can set mMaxHeight, make sure the corresponding  
  87.             // XML attribute for mMaxHeight is read after calling this method  
  88.             setProgressDrawable(drawable);  
  89.         }  
  90.   
  91.   
  92.         mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_android_minWidth, mMinWidth);  
  93.         mMaxWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_android_maxWidth, mMaxWidth);  
  94.         mMinHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_android_minHeight, mMinHeight);  
  95.         mMaxHeight = a.getDimensionPixelSize(R.styleable.ProgressBar_android_maxHeight, mMaxHeight);  
  96.   
  97.         setMax(a.getInt(R.styleable.ProgressBar_android_max, mMax));  
  98.   
  99.         setProgress(a.getInt(R.styleable.ProgressBar_android_progress, mProgress));  
  100.   
  101.         setSecondaryProgress(  
  102.                 a.getInt(R.styleable.ProgressBar_android_secondaryProgress, mSecondaryProgress));  
  103.   
  104.         mNoInvalidate = false;  
  105.   
  106.         a.recycle();  
  107.     }  
  108.   
  109.     /** 
  110.      * Converts a drawable to a tiled version of itself. It will recursively 
  111.      * traverse layer and state list drawables. 
  112.      */  
  113.     private Drawable tileify(Drawable drawable, boolean clip) {  
  114.   
  115.         if (drawable instanceof LayerDrawable) {  
  116.             LayerDrawable background = (LayerDrawable) drawable;  
  117.             final int N = background.getNumberOfLayers();  
  118.             Drawable[] outDrawables = new Drawable[N];  
  119.   
  120.             for (int i = 0; i < N; i++) {  
  121.                 int id = background.getId(i);  
  122.                 outDrawables[i] = tileify(background.getDrawable(i),  
  123.                         (id == android.R.id.progress || id == android.R.id.secondaryProgress));  
  124.             }  
  125.   
  126.             LayerDrawable newBg = new LayerDrawable(outDrawables);  
  127.   
  128.             for (int i = 0; i < N; i++) {  
  129.                 newBg.setId(i, background.getId(i));  
  130.             }  
  131.   
  132.             return newBg;  
  133.   
  134.         } else if (drawable instanceof StateListDrawable) {  
  135.             StateListDrawable in = (StateListDrawable) drawable;  
  136.             StateListDrawable out = new StateListDrawable();  
  137.             /*int numStates = in.getStateCount(); 
  138.             for (int i = 0; i < numStates; i++) { 
  139.                 out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip)); 
  140.             }*/  
  141.             return out;  
  142.   
  143.         } else if (drawable instanceof BitmapDrawable) {  
  144.             final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();  
  145.             if (mSampleTile == null) {  
  146.                 mSampleTile = tileBitmap;  
  147.             }  
  148.   
  149.             final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());  
  150.             return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,  
  151.                     ClipDrawable.HORIZONTAL) : shapeDrawable;  
  152.         }  
  153.   
  154.         return drawable;  
  155.     }  
  156.   
  157.     Shape getDrawableShape() {  
  158.         final float[] roundedCorners = new float[] { 55555555 };  
  159.         return new RoundRectShape(roundedCorners, nullnull);  
  160.     }  
  161.   
  162.     /** 
  163.      * <p> 
  164.      * Initialize the progress bar's default values: 
  165.      * </p> 
  166.      * <ul> 
  167.      * <li>progress = 0</li> 
  168.      * <li>max = 100</li> 
  169.      * </ul> 
  170.      */  
  171.     private void initProgressBar() {  
  172.         mMax = 100;  
  173.         mProgress = 0;  
  174.         mSecondaryProgress = 0;  
  175.         mMinWidth = 24;  
  176.         mMaxWidth = 48;  
  177.         mMinHeight = 24;  
  178.         mMaxHeight = 48;  
  179.     }  
  180.   
  181.     /** 
  182.      * <p>Get the drawable used to draw the progress bar in 
  183.      * progress mode.</p> 
  184.      * 
  185.      * @return a {@link android.graphics.drawable.Drawable} instance 
  186.      * 
  187.      * @see #setProgressDrawable(android.graphics.drawable.Drawable) 
  188.      */  
  189.     public Drawable getProgressDrawable() {  
  190.         return mProgressDrawable;  
  191.     }  
  192.   
  193.     /** 
  194.      * <p>Define the drawable used to draw the progress bar in 
  195.      * progress mode.</p> 
  196.      * 
  197.      * @param d the new drawable 
  198.      * 
  199.      * @see #getProgressDrawable() 
  200.      */  
  201.     public void setProgressDrawable(Drawable d) {  
  202.         if (d != null) {  
  203.             d.setCallback(this);  
  204.             // Make sure the ProgressBar is always tall enough  
  205.             int drawableHeight = d.getMinimumHeight();  
  206.             if (mMaxHeight < drawableHeight) {  
  207.                 mMaxHeight = drawableHeight;  
  208.                 requestLayout();  
  209.             }  
  210.         }  
  211.         mProgressDrawable = d;  
  212.         mCurrentDrawable = d;  
  213.         postInvalidate();  
  214.     }  
  215.   
  216.     /** 
  217.      * @return The drawable currently used to draw the progress bar 
  218.      */  
  219.     Drawable getCurrentDrawable() {  
  220.         return mCurrentDrawable;  
  221.     }  
  222.   
  223.     @Override  
  224.     protected boolean verifyDrawable(Drawable who) {  
  225.         return who == mProgressDrawable || super.verifyDrawable(who);  
  226.     }  
  227.   
  228.     @Override  
  229.     public void postInvalidate() {  
  230.         if (!mNoInvalidate) {  
  231.             super.postInvalidate();  
  232.         }  
  233.     }  
  234.   
  235.     private class RefreshProgressRunnable implements Runnable {  
  236.   
  237.         private int mId;  
  238.         private int mProgress;  
  239.         private boolean mFromUser;  
  240.   
  241.         RefreshProgressRunnable(int id, int progress, boolean fromUser) {  
  242.             mId = id;  
  243.             mProgress = progress;  
  244.             mFromUser = fromUser;  
  245.         }  
  246.   
  247.         public void run() {  
  248.             doRefreshProgress(mId, mProgress, mFromUser);  
  249.             // Put ourselves back in the cache when we are done  
  250.             mRefreshProgressRunnable = this;  
  251.         }  
  252.   
  253.         public void setup(int id, int progress, boolean fromUser) {  
  254.             mId = id;  
  255.             mProgress = progress;  
  256.             mFromUser = fromUser;  
  257.         }  
  258.   
  259.     }  
  260.   
  261.     private synchronized void doRefreshProgress(int id, int progress, boolean fromUser) {  
  262.         float scale = mMax > 0 ? (float) progress / (float) mMax : 0;  
  263.         final Drawable d = mCurrentDrawable;  
  264.         if (d != null) {  
  265.             Drawable progressDrawable = null;  
  266.   
  267.             if (d instanceof LayerDrawable) {  
  268.                 progressDrawable = ((LayerDrawable) d).findDrawableByLayerId(id);  
  269.             }  
  270.   
  271.             final int level = (int) (scale * MAX_LEVEL);  
  272.             (progressDrawable != null ? progressDrawable : d).setLevel(level);  
  273.         } else {  
  274.             invalidate();  
  275.         }  
  276.   
  277.         if (id == android.R.id.progress) {  
  278.             onProgressRefresh(scale, fromUser);  
  279.         }  
  280.     }  
  281.   
  282.     void onProgressRefresh(float scale, boolean fromUser) {  
  283.     }  
  284.   
  285.     private synchronized void refreshProgress(int id, int progress, boolean fromUser) {  
  286.         if (mUiThreadId == Thread.currentThread().getId()) {  
  287.             doRefreshProgress(id, progress, fromUser);  
  288.         } else {  
  289.             RefreshProgressRunnable r;  
  290.             if (mRefreshProgressRunnable != null) {  
  291.                 // Use cached RefreshProgressRunnable if available  
  292.                 r = mRefreshProgressRunnable;  
  293.                 // Uncache it  
  294.                 mRefreshProgressRunnable = null;  
  295.                 r.setup(id, progress, fromUser);  
  296.             } else {  
  297.                 // Make a new one  
  298.                 r = new RefreshProgressRunnable(id, progress, fromUser);  
  299.             }  
  300.             post(r);  
  301.         }  
  302.     }  
  303.   
  304.     /** 
  305.      * <p>Set the current progress to the specified value.</p> 
  306.      * 
  307.      * @param progress the new progress, between 0 and {@link #getMax()} 
  308.      * 
  309.      * @see #getProgress() 
  310.      * @see #incrementProgressBy(int) 
  311.      */  
  312.     public synchronized void setProgress(int progress) {  
  313.         setProgress(progress, false);  
  314.     }  
  315.   
  316.     synchronized void setProgress(int progress, boolean fromUser) {  
  317.         if (progress < 0) {  
  318.             progress = 0;  
  319.         }  
  320.   
  321.         if (progress > mMax) {  
  322.             progress = mMax;  
  323.         }  
  324.   
  325.         if (progress != mProgress) {  
  326.             mProgress = progress;  
  327.             refreshProgress(android.R.id.progress, mProgress, fromUser);  
  328.         }  
  329.     }  
  330.   
  331.     /** 
  332.      * <p> 
  333.      * Set the current secondary progress to the specified value. 
  334.      * </p> 
  335.      * 
  336.      * @param secondaryProgress the new secondary progress, between 0 and {@link #getMax()} 
  337.      * @see #getSecondaryProgress() 
  338.      * @see #incrementSecondaryProgressBy(int) 
  339.      */  
  340.     public synchronized void setSecondaryProgress(int secondaryProgress) {  
  341.         if (secondaryProgress < 0) {  
  342.             secondaryProgress = 0;  
  343.         }  
  344.   
  345.         if (secondaryProgress > mMax) {  
  346.             secondaryProgress = mMax;  
  347.         }  
  348.   
  349.         if (secondaryProgress != mSecondaryProgress) {  
  350.             mSecondaryProgress = secondaryProgress;  
  351.             refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false);  
  352.         }  
  353.     }  
  354.   
  355.     /** 
  356.      * <p>Get the progress bar's current level of progress.</p> 
  357.      * 
  358.      * @return the current progress, between 0 and {@link #getMax()} 
  359.      * 
  360.      * @see #setProgress(int) 
  361.      * @see #setMax(int) 
  362.      * @see #getMax() 
  363.      */  
  364.     @ViewDebug.ExportedProperty  
  365.     public synchronized int getProgress() {  
  366.         return mProgress;  
  367.     }  
  368.   
  369.     /** 
  370.      * <p>Get the progress bar's current level of secondary progress.</p> 
  371.      * 
  372.      * @return the current secondary progress, between 0 and {@link #getMax()} 
  373.      * 
  374.      * @see #setSecondaryProgress(int) 
  375.      * @see #setMax(int) 
  376.      * @see #getMax() 
  377.      */  
  378.     @ViewDebug.ExportedProperty  
  379.     public synchronized int getSecondaryProgress() {  
  380.         return mSecondaryProgress;  
  381.     }  
  382.   
  383.     /** 
  384.      * <p>Return the upper limit of this progress bar's range.</p> 
  385.      * 
  386.      * @return a positive integer 
  387.      * 
  388.      * @see #setMax(int) 
  389.      * @see #getProgress() 
  390.      * @see #getSecondaryProgress() 
  391.      */  
  392.     @ViewDebug.ExportedProperty  
  393.     public synchronized int getMax() {  
  394.         return mMax;  
  395.     }  
  396.   
  397.     /** 
  398.      * <p>Set the range of the progress bar to 0...<tt>max</tt>.</p> 
  399.      * 
  400.      * @param max the upper range of this progress bar 
  401.      * 
  402.      * @see #getMax() 
  403.      * @see #setProgress(int) 
  404.      * @see #setSecondaryProgress(int) 
  405.      */  
  406.     public synchronized void setMax(int max) {  
  407.         if (max < 0) {  
  408.             max = 0;  
  409.         }  
  410.         if (max != mMax) {  
  411.             mMax = max;  
  412.             postInvalidate();  
  413.   
  414.             if (mProgress > max) {  
  415.                 mProgress = max;  
  416.                 refreshProgress(android.R.id.progress, mProgress, false);  
  417.             }  
  418.         }  
  419.     }  
  420.   
  421.     /** 
  422.      * <p>Increase the progress bar's progress by the specified amount.</p> 
  423.      * 
  424.      * @param diff the amount by which the progress must be increased 
  425.      * 
  426.      * @see #setProgress(int) 
  427.      */  
  428.     public synchronized final void incrementProgressBy(int diff) {  
  429.         setProgress(mProgress + diff);  
  430.     }  
  431.   
  432.     /** 
  433.      * <p>Increase the progress bar's secondary progress by the specified amount.</p> 
  434.      * 
  435.      * @param diff the amount by which the secondary progress must be increased 
  436.      * 
  437.      * @see #setSecondaryProgress(int) 
  438.      */  
  439.     public synchronized final void incrementSecondaryProgressBy(int diff) {  
  440.         setSecondaryProgress(mSecondaryProgress + diff);  
  441.     }  
  442.   
  443.     @Override  
  444.     public void setVisibility(int v) {  
  445.         if (getVisibility() != v) {  
  446.             super.setVisibility(v);  
  447.         }  
  448.     }  
  449.   
  450.     @Override  
  451.     public void invalidateDrawable(Drawable dr) {  
  452.         if (!mInDrawing) {  
  453.             if (verifyDrawable(dr)) {  
  454.                 final Rect dirty = dr.getBounds();  
  455.                 final int scrollX = mScrollX + mPaddingLeft;  
  456.                 final int scrollY = mScrollY + mPaddingTop;  
  457.   
  458.                 invalidate(dirty.left + scrollX, dirty.top + scrollY,  
  459.                         dirty.right + scrollX, dirty.bottom + scrollY);  
  460.             } else {  
  461.                 super.invalidateDrawable(dr);  
  462.             }  
  463.         }  
  464.     }  
  465.   
  466.     @Override  
  467.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  468.         // onDraw will translate the canvas so we draw starting at 0,0  
  469.         int right = w - mPaddingRight - mPaddingLeft;  
  470.         int bottom = h - mPaddingBottom - mPaddingTop;  
  471.   
  472.         if (mProgressDrawable != null) {  
  473.             mProgressDrawable.setBounds(00, right, bottom);  
  474.         }  
  475.     }  
  476.   
  477.     @Override  
  478.     protected synchronized void onDraw(Canvas canvas) {  
  479.         super.onDraw(canvas);  
  480.   
  481.         Drawable d = mCurrentDrawable;  
  482.         if (d != null) {  
  483.             // Translate canvas so a indeterminate circular progress bar with padding  
  484.             // rotates properly in its animation  
  485.             canvas.save();  
  486.             canvas.translate(mPaddingLeft, mPaddingTop);  
  487.             d.draw(canvas);  
  488.             canvas.restore();  
  489.         }  
  490.     }  
  491.   
  492.     @Override  
  493.     protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  494.         Drawable d = mCurrentDrawable;  
  495.   
  496.         int dw = 0;  
  497.         int dh = 0;  
  498.         if (d != null) {  
  499.             dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));  
  500.             dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));  
  501.         }  
  502.         dw += mPaddingLeft + mPaddingRight;  
  503.         dh += mPaddingTop + mPaddingBottom;  
  504.   
  505.         setMeasuredDimension(resolveSize(dw, widthMeasureSpec),  
  506.                 resolveSize(dh, heightMeasureSpec));  
  507.     }  
  508.   
  509.     @Override  
  510.     protected void drawableStateChanged() {  
  511.         super.drawableStateChanged();  
  512.   
  513.         int[] state = getDrawableState();  
  514.   
  515.         if (mProgressDrawable != null && mProgressDrawable.isStateful()) {  
  516.             mProgressDrawable.setState(state);  
  517.         }  
  518.     }  
  519.   
  520.     static class SavedState extends BaseSavedState {  
  521.         int progress;  
  522.         int secondaryProgress;  
  523.   
  524.         /** 
  525.          * Constructor called from {@link ProgressBar#onSaveInstanceState()} 
  526.          */  
  527.         SavedState(Parcelable superState) {  
  528.             super(superState);  
  529.         }  
  530.   
  531.         /** 
  532.          * Constructor called from {@link #CREATOR} 
  533.          */  
  534.         private SavedState(Parcel in) {  
  535.             super(in);  
  536.             progress = in.readInt();  
  537.             secondaryProgress = in.readInt();  
  538.         }  
  539.   
  540.         @Override  
  541.         public void writeToParcel(Parcel out, int flags) {  
  542.             super.writeToParcel(out, flags);  
  543.             out.writeInt(progress);  
  544.             out.writeInt(secondaryProgress);  
  545.         }  
  546.   
  547.         public static final Parcelable.Creator<savedstate> CREATOR  
  548.                 = new Parcelable.Creator<savedstate>() {  
  549.             public SavedState createFromParcel(Parcel in) {  
  550.                 return new SavedState(in);  
  551.             }  
  552.   
  553.             public SavedState[] newArray(int size) {  
  554.                 return new SavedState[size];  
  555.             }  
  556.         };  
  557.     }  
  558.   
  559.     @Override  
  560.     public Parcelable onSaveInstanceState() {  
  561.         // Force our ancestor class to save its state  
  562.         Parcelable superState = super.onSaveInstanceState();  
  563.         SavedState ss = new SavedState(superState);  
  564.   
  565.         ss.progress = mProgress;  
  566.         ss.secondaryProgress = mSecondaryProgress;  
  567.   
  568.         return ss;  
  569.     }  
  570.   
  571.     @Override  
  572.     public void onRestoreInstanceState(Parcelable state) {  
  573.         SavedState ss = (SavedState) state;  
  574.         super.onRestoreInstanceState(ss.getSuperState());  
  575.   
  576.         setProgress(ss.progress);  
  577.         setSecondaryProgress(ss.secondaryProgress);  
  578.     }  
  579. }  
  580.   
  581. </savedstate></savedstate>  

리스너를 정의한 클래스이다. VerticalSeekBar.Java
  1. package com.ememomo.tistory;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5.   
  6.   
  7.   
  8. /** 
  9.  * A SeekBar is an extension of ProgressBar that adds a draggable thumb. The user can touch 
  10.  * the thumb and drag left or right to set the current progress level or use the arrow keys. 
  11.  * Placing focusable widgets to the left or right of a SeekBar is discouraged. 
  12.  * <p> 
  13.  * Clients of the SeekBar can attach a {@link SeekBar.OnSeekBarChangeListener} to 
  14.  * be notified of the user's actions. 
  15.  * 
  16.  * @attr ref android.R.styleable#SeekBar_thumb 
  17.  */  
  18. public class VerticalSeekBar extends AbsVerticalSeekBar {  
  19.   
  20.     /** 
  21.      * A callback that notifies clients when the progress level has been 
  22.      * changed. This includes changes that were initiated by the user through a 
  23.      * touch gesture or arrow key/trackball as well as changes that were initiated 
  24.      * programmatically. 
  25.      */  
  26.     public interface OnSeekBarChangeListener {  
  27.   
  28.         /** 
  29.          * Notification that the progress level has changed. Clients can use the fromUser parameter 
  30.          * to distinguish user-initiated changes from those that occurred programmatically. 
  31.          * 
  32.          * @param seekBar The SeekBar whose progress has changed 
  33.          * @param progress The current progress level. This will be in the range 0..max where max 
  34.          *        was set by {@link ProgressBar#setMax(int)}. (The default value for max is 100.) 
  35.          * @param fromUser True if the progress change was initiated by the user. 
  36.          */  
  37.         void onProgressChanged(VerticalSeekBar seekBar, int progress, boolean fromUser);  
  38.   
  39.         /** 
  40.          * Notification that the user has started a touch gesture. Clients may want to use this 
  41.          * to disable advancing the seekbar. 
  42.          * @param seekBar The SeekBar in which the touch gesture began 
  43.          */  
  44.         void onStartTrackingTouch(VerticalSeekBar seekBar);  
  45.   
  46.         /** 
  47.          * Notification that the user has finished a touch gesture. Clients may want to use this 
  48.          * to re-enable advancing the seekbar. 
  49.          * @param seekBar The SeekBar in which the touch gesture began 
  50.          */  
  51.         void onStopTrackingTouch(VerticalSeekBar seekBar);  
  52.     }  
  53.   
  54.     private OnSeekBarChangeListener mOnSeekBarChangeListener;  
  55.   
  56.     public VerticalSeekBar(Context context) {  
  57.         this(context, null);  
  58.     }  
  59.   
  60.     public VerticalSeekBar(Context context, AttributeSet attrs) {  
  61.         this(context, attrs, android.R.attr.seekBarStyle);  
  62.     }  
  63.   
  64.     public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {  
  65.         super(context, attrs, defStyle);  
  66.     }  
  67.   
  68.     @Override  
  69.     void onProgressRefresh(float scale, boolean fromUser) {  
  70.         super.onProgressRefresh(scale, fromUser);  
  71.   
  72.         if (mOnSeekBarChangeListener != null) {  
  73.             mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * Sets a listener to receive notifications of changes to the SeekBar's progress level. Also 
  79.      * provides notifications of when the user starts and stops a touch gesture within the SeekBar. 
  80.      * 
  81.      * @param l The seek bar notification listener 
  82.      * 
  83.      * @see SeekBar.OnSeekBarChangeListener 
  84.      */  
  85.     public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {  
  86.         mOnSeekBarChangeListener = l;  
  87.     }  
  88.   
  89.     @Override  
  90.     void onStartTrackingTouch() {  
  91.         if (mOnSeekBarChangeListener != null) {  
  92.             mOnSeekBarChangeListener.onStartTrackingTouch(this);  
  93.         }  
  94.     }  
  95.   
  96.     @Override  
  97.     void onStopTrackingTouch() {  
  98.         if (mOnSeekBarChangeListener != null) {  
  99.             mOnSeekBarChangeListener.onStopTrackingTouch(this);  
  100.         }  
  101.     }  
  102.   
  103. }  
  104.   
  105. </p>  
두개의 클래스를 이용하여 기능을 정의하는 클래스 AbsVerticalSeekBar.Java
  1. package com.ememomo.tistory;  
  2.   
  3. import com.tokaracamara.android.verticalslidevar.R;  
  4.   
  5. import android.content.Context;  
  6. import android.content.res.TypedArray;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Rect;  
  9. import android.graphics.drawable.Drawable;  
  10. import android.util.AttributeSet;  
  11. import android.util.Log;  
  12. import android.view.KeyEvent;  
  13. import android.view.MotionEvent;  
  14.   
  15. public class AbsVerticalSeekBar extends VerticalProgressBar{  
  16.   
  17.     private Drawable mThumb;  
  18.     private int mThumbOffset;  
  19.   
  20.     /** 
  21.      * On touch, this offset plus the scaled value from the position of the 
  22.      * touch will form the progress value. Usually 0. 
  23.      */  
  24.     float mTouchProgressOffset;  
  25.   
  26.     /** 
  27.      * Whether this is user seekable. 
  28.      */  
  29.     boolean mIsUserSeekable = true;  
  30.   
  31.     /** 
  32.      * On key presses (right or left), the amount to increment/decrement the 
  33.      * progress. 
  34.      */  
  35.     private int mKeyProgressIncrement = 1;  
  36.   
  37.     private static final int NO_ALPHA = 0xFF;  
  38.     private float mDisabledAlpha;  
  39.   
  40.     public AbsVerticalSeekBar(Context context) {  
  41.         super(context);  
  42.     }  
  43.   
  44.     public AbsVerticalSeekBar(Context context, AttributeSet attrs) {  
  45.         super(context, attrs);  
  46.     }  
  47.   
  48.     public AbsVerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {  
  49.         super(context, attrs, defStyle);  
  50.   
  51.         TypedArray a = context.obtainStyledAttributes(attrs,  
  52.                 R.styleable.SeekBar, defStyle, 0);  
  53.         Drawable thumb = a.getDrawable(R.styleable.SeekBar_android_thumb);  
  54.         setThumb(thumb); // will guess mThumbOffset if thumb != null...  
  55.         // ...but allow layout to override this  
  56.         int thumbOffset =  
  57.                 a.getDimensionPixelOffset(R.styleable.SeekBar_android_thumbOffset, getThumbOffset());  
  58.         setThumbOffset(thumbOffset);  
  59.         a.recycle();  
  60.   
  61.         a = context.obtainStyledAttributes(attrs,  
  62.                 R.styleable.Theme, 00);  
  63.         mDisabledAlpha = a.getFloat(R.styleable.Theme_android_disabledAlpha, 0.5f);  
  64.         a.recycle();  
  65.     }  
  66.   
  67.     /** 
  68.      * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar. 
  69.      * <p> 
  70.      * If the thumb is a valid drawable (i.e. not null), half its width will be 
  71.      * used as the new thumb offset (@see #setThumbOffset(int)). 
  72.      * 
  73.      * @param thumb Drawable representing the thumb 
  74.      */  
  75.     public void setThumb(Drawable thumb) {  
  76.         if (thumb != null) {  
  77.             thumb.setCallback(this);  
  78.   
  79.             // Assuming the thumb drawable is symmetric, set the thumb offset  
  80.             // such that the thumb will hang halfway off either edge of the  
  81.             // progress bar.  
  82.             mThumbOffset = (int)thumb.getIntrinsicHeight() / 2;  
  83.         }  
  84.         mThumb = thumb;  
  85.         invalidate();  
  86.     }  
  87.   
  88.     /** 
  89.      * @see #setThumbOffset(int) 
  90.      */  
  91.     public int getThumbOffset() {  
  92.         return mThumbOffset;  
  93.     }  
  94.   
  95.     /** 
  96.      * Sets the thumb offset that allows the thumb to extend out of the range of 
  97.      * the track. 
  98.      * 
  99.      * @param thumbOffset The offset amount in pixels. 
  100.      */  
  101.     public void setThumbOffset(int thumbOffset) {  
  102.         mThumbOffset = thumbOffset;  
  103.         invalidate();  
  104.     }  
  105.   
  106.     /** 
  107.      * Sets the amount of progress changed via the arrow keys. 
  108.      * 
  109.      * @param increment The amount to increment or decrement when the user 
  110.      *            presses the arrow keys. 
  111.      */  
  112.     public void setKeyProgressIncrement(int increment) {  
  113.         mKeyProgressIncrement = increment < 0 ? -increment : increment;  
  114.     }  
  115.   
  116.     /** 
  117.      * Returns the amount of progress changed via the arrow keys. 
  118.      * </p><p> 
  119.      * By default, this will be a value that is derived from the max progress. 
  120.      * 
  121.      * @return The amount to increment or decrement when the user presses the 
  122.      *         arrow keys. This will be positive. 
  123.      */  
  124.     public int getKeyProgressIncrement() {  
  125.         return mKeyProgressIncrement;  
  126.     }  
  127.   
  128.     @Override  
  129.     public synchronized void setMax(int max) {  
  130.         super.setMax(max);  
  131.   
  132.         if ((mKeyProgressIncrement == 0) || (getMax() / mKeyProgressIncrement > 20)) {  
  133.             // It will take the user too long to change this via keys, change it  
  134.             // to something more reasonable  
  135.             setKeyProgressIncrement(Math.max(1, Math.round((float) getMax() / 20)));  
  136.         }  
  137.     }  
  138.   
  139.     @Override  
  140.     protected boolean verifyDrawable(Drawable who) {  
  141.         return who == mThumb || super.verifyDrawable(who);  
  142.     }  
  143.   
  144.     @Override  
  145.     protected void drawableStateChanged() {  
  146.         super.drawableStateChanged();  
  147.   
  148.         Drawable progressDrawable = getProgressDrawable();  
  149.         if (progressDrawable != null) {  
  150.             progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));  
  151.         }  
  152.   
  153.         if (mThumb != null && mThumb.isStateful()) {  
  154.             int[] state = getDrawableState();  
  155.             mThumb.setState(state);  
  156.         }  
  157.     }  
  158.   
  159.     @Override  
  160.     void onProgressRefresh(float scale, boolean fromUser) {  
  161.         Drawable thumb = mThumb;  
  162.         if (thumb != null) {  
  163.             setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);  
  164.             /* 
  165.              * Since we draw translated, the drawable's bounds that it signals 
  166.              * for invalidation won't be the actual bounds we want invalidated, 
  167.              * so just invalidate this whole view. 
  168.              */  
  169.             invalidate();  
  170.         }  
  171.     }  
  172.   
  173.   
  174.     @Override  
  175.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  176.         Drawable d = getCurrentDrawable();  
  177.         Drawable thumb = mThumb;  
  178.         int thumbWidth = thumb == null ? 0 : thumb.getIntrinsicWidth();  
  179.         // The max height does not incorporate padding, whereas the height  
  180.         // parameter does  
  181.         int trackWidth = Math.min(mMaxWidth, w - mPaddingRight - mPaddingLeft);  
  182.         int max = getMax();  
  183.         float scale = max > 0 ? (float) getProgress() / (float) max : 0;  
  184.   
  185.         if (thumbWidth > trackWidth) {  
  186.             int gapForCenteringTrack = (thumbWidth - trackWidth) / 2;  
  187.             if (thumb != null) {  
  188.                 setThumbPos(h, thumb, scale, gapForCenteringTrack * -1);  
  189.             }  
  190.             if (d != null) {  
  191.                 // Canvas will be translated by the padding, so 0,0 is where we start drawing  
  192.                 d.setBounds(gapForCenteringTrack, 0,  
  193.                         w - mPaddingRight - mPaddingLeft - gapForCenteringTrack,  
  194.                         h - mPaddingBottom - mPaddingTop);  
  195.             }  
  196.         } else {  
  197.             if (d != null) {  
  198.                 // Canvas will be translated by the padding, so 0,0 is where we start drawing  
  199.                 d.setBounds(00, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - mPaddingTop);  
  200.             }  
  201.             int gap = (trackWidth - thumbWidth) / 2;  
  202.             if (thumb != null) {  
  203.                 setThumbPos(h, thumb, scale, gap);  
  204.             }  
  205.         }  
  206.     }  
  207.   
  208.     /** 
  209.      * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and 
  210.      */  
  211.     private void setThumbPos(int h, Drawable thumb, float scale, int gap) {  
  212.         int available = h - mPaddingTop - mPaddingBottom;  
  213.         int thumbWidth = thumb.getIntrinsicWidth();  
  214.         int thumbHeight = thumb.getIntrinsicHeight();  
  215.         available -= thumbHeight;  
  216.   
  217.         // The extra space for the thumb to move on the track  
  218.         available += mThumbOffset * 2;  
  219.         int thumbPos = (int) ((1-scale) * available);  
  220.         int leftBound, rightBound;  
  221.         if (gap == Integer.MIN_VALUE) {  
  222.             Rect oldBounds = thumb.getBounds();  
  223.             leftBound = oldBounds.left;  
  224.             rightBound = oldBounds.right;  
  225.         } else {  
  226.             leftBound = gap;  
  227.             rightBound = gap + thumbWidth;  
  228.         }  
  229.   
  230.         // Canvas will be translated, so 0,0 is where we start drawing  
  231.         thumb.setBounds(leftBound, thumbPos, rightBound, thumbPos + thumbHeight);  
  232.     }  
  233.   
  234.     @Override  
  235.     protected synchronized void onDraw(Canvas canvas) {  
  236.         super.onDraw(canvas);  
  237.         if (mThumb != null) {  
  238.             canvas.save();  
  239.             // Translate the padding. For the x, we need to allow the thumb to  
  240.             // draw in its extra space  
  241.             canvas.translate(mPaddingLeft, mPaddingTop - mThumbOffset);  
  242.             mThumb.draw(canvas);  
  243.             canvas.restore();  
  244.         }  
  245.     }  
  246.   
  247.     @Override  
  248.     protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  249.         Drawable d = getCurrentDrawable();  
  250.   
  251.         int thumbWidth = mThumb == null ? 0 : mThumb.getIntrinsicWidth();  
  252.         int dw = 0;  
  253.         int dh = 0;  
  254.         if (d != null) {  
  255.             dw = Math.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));  
  256.             dw = Math.max(thumbWidth, dh);  
  257.             dh = Math.max(mMinHeight, Math.min(mMaxHeight, d.getIntrinsicHeight()));  
  258.         }  
  259.         dw += mPaddingLeft + mPaddingRight;  
  260.         dh += mPaddingTop + mPaddingBottom;  
  261.   
  262.         setMeasuredDimension(resolveSize(dw, widthMeasureSpec),  
  263.                 resolveSize(dh, heightMeasureSpec));  
  264.     }  
  265.   
  266.     @Override  
  267.     public boolean onTouchEvent(MotionEvent event) {  
  268.         if (!mIsUserSeekable || !isEnabled()) {  
  269.             return false;  
  270.         }  
  271.   
  272.         switch (event.getAction()) {  
  273.             case MotionEvent.ACTION_DOWN:  
  274.                 setPressed(true);  
  275.                 onStartTrackingTouch();  
  276.                 trackTouchEvent(event);  
  277.                 break;  
  278.   
  279.             case MotionEvent.ACTION_MOVE:  
  280.                 trackTouchEvent(event);  
  281.                 attemptClaimDrag();  
  282.                 break;  
  283.   
  284.             case MotionEvent.ACTION_UP:  
  285.                 trackTouchEvent(event);  
  286.                 onStopTrackingTouch();  
  287.                 setPressed(false);  
  288.                 // ProgressBar doesn't know to repaint the thumb drawable  
  289.                 // in its inactive state when the touch stops (because the  
  290.                 // value has not apparently changed)  
  291.                 invalidate();  
  292.                 break;  
  293.   
  294.             case MotionEvent.ACTION_CANCEL:  
  295.                 onStopTrackingTouch();  
  296.                 setPressed(false);  
  297.                 invalidate(); // see above explanation  
  298.                 break;  
  299.         }  
  300.         return true;  
  301.     }  
  302.   
  303.     private void trackTouchEvent(MotionEvent event) {  
  304.         final int height = getHeight();  
  305.         final int available = height - mPaddingTop - mPaddingBottom;  
  306.         int y = height - (int)event.getY();  
  307.         float scale;  
  308.         float progress = 0;  
  309.         if (y < mPaddingBottom) {  
  310.             scale = 0.0f;  
  311.         } else if (y > height - mPaddingTop) {  
  312.             scale = 1.0f;  
  313.         } else {  
  314.             scale = (float)(y - mPaddingBottom) / (float)available;  
  315.             progress = mTouchProgressOffset;  
  316.         }  
  317.   
  318.         final int max = getMax();  
  319.         progress += scale * max;  
  320.   
  321.         setProgress((int) progress, true);  
  322.     }  
  323.   
  324.     /** 
  325.      * Tries to claim the user's drag motion, and requests disallowing any 
  326.      * ancestors from stealing events in the drag. 
  327.      */  
  328.     private void attemptClaimDrag() {  
  329.         if (mParent != null) {  
  330.             mParent.requestDisallowInterceptTouchEvent(true);  
  331.         }  
  332.     }  
  333.   
  334.     /** 
  335.      * This is called when the user has started touching this widget. 
  336.      */  
  337.     void onStartTrackingTouch() {  
  338.     }  
  339.   
  340.     /** 
  341.      * This is called when the user either releases his touch or the touch is 
  342.      * canceled. 
  343.      */  
  344.     void onStopTrackingTouch() {  
  345.     }  
  346.   
  347.     /** 
  348.      * Called when the user changes the seekbar's progress by using a key event. 
  349.      */  
  350.     void onKeyChange() {  
  351.     }  
  352.   
  353.     @Override  
  354.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  355.         int progress = getProgress();  
  356.   
  357.         switch (keyCode) {  
  358.             case KeyEvent.KEYCODE_DPAD_DOWN:  
  359.                 if (progress <= 0break;  
  360.                 setProgress(progress - mKeyProgressIncrement, true);  
  361.                 onKeyChange();  
  362.                 return true;  
  363.   
  364.             case KeyEvent.KEYCODE_DPAD_UP:  
  365.                 if (progress >= getMax()) break;  
  366.                 setProgress(progress + mKeyProgressIncrement, true);  
  367.                 onKeyChange();  
  368.                 return true;  
  369.         }  
  370.   
  371.         return super.onKeyDown(keyCode, event);  
  372.     }  
  373.   
  374. }  
  375. </p>  
프로그래스바를 세로로 만들뿐인데 상당히 복잡하다.. 이 안에 내용에 대해선 각자 분석해 보길 바란다. 본인도 그저 가져다 쓸 뿐이므로. 그리 고민하지 않았다.. 기능에 대해 자세한 설명은 Comment 로 남겨 주면 매우 감사~~ 이제 위 화면을 띄우기 위한 Activity에 대해 설명하겠다. 먼저 세로로 그릴 Progress 내의 커스터 마이징은 xml로 하였다. progress_vertical.xml 이 XML을 이용하여 뒷부분의 색과 안에 채워질 Bar에 대한 컬러를 정의한다.
  1. <!--?xml version="1.0" encoding="utf-8"?-->  
  2. <layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
  3.   
  4.     <item android:id="@android:id/background">  
  5.         <shape>  
  6.             <corners android:radius="5dip">  
  7.             <gradient android:startcolor="#ffffff" android:endcolor="#ffffff" android:centercolor="#ffffff" android:centery="0.75" android:angle="0">  
  8.         </gradient></corners></shape>  
  9.     </item>  
  10.     <item android:id="@android:id/secondaryProgress">  
  11.         <clip android:cliporientation="vertical" android:gravity="bottom">  
  12.             <shape>  
  13.                 <corners android:radius="5dip">  
  14.                 <gradient android:startcolor="#bebebe" android:endcolor="#bebebe" android:centercolor="#bebebe" android:centery="0.75" android:angle="0">  
  15.             </gradient></corners></shape>  
  16.         </clip>   
  17.     </item>  
  18.   
  19.     <item android:id="@android:id/progress">  
  20.         <clip android:cliporientation="vertical" android:gravity="bottom">  
  21.             <shape>  
  22.                 <corners android:radius="5dip">  
  23.                 <gradient android:startcolor="#828282" android:endcolor="#828282" android:centercolor="#828282" android:centery="0.75" android:angle="0">  
  24.             </gradient></corners></shape>  
  25.         </clip>  
  26.     </item>  
  27. </layer-list>  
View로 쓰일 XML 화면을 만든 XML이다. main.xml
  1. <!--?xml version="1.0" encoding="utf-8"?-->  
  2. <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent">  
  3.     android:layout_height="fill_parent">  
  4.     <textview android:gravity="center" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textsize="30sp" android:textstyle="bold" android:text="WelCome EpoNg's Blog">  
  5.     <linearlayout android:layout_width="wrap_content" android:layout_gravity="center" android:layout_margintop="10dip" android:layout_marginleft="10dip" android:layout_height="200dip" android:orientation="vertical">  
  6.         <com.ememomo.tistory.verticalseekbar android:id="@+id/vertical_seekbar" android:progressdrawable="@drawable/myprogress" android:thumb="@drawable/time_bar_btn" android:layout_width="18dip" android:layout_height="fill_parent" android:thumboffset="0dip">  
  7.     </com.ememomo.tistory.verticalseekbar></linearlayout>  
  8.         <textview android:id="@+id/progress" android:gravity="center" android:layout_margintop="10dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="111">  
  9.         <seekbar android:id="@+id/horizontal_seekbar" android:visibility="visible" android:layout_gravity="center_horizontal" android:layout_width="200dip" android:layout_height="wrap_content" android:maxheight="7.3dip" android:paddingleft="40dip" android:paddingright="40dip" android:layout_marginbottom="5dip" android:max="15" android:thumb="@drawable/time_bar_btn" style="@style/EpoNgsProgressStyle" android:thumboffset="0.7dip">  
  10.         <textview android:id="@+id/horizon_progress" android:gravity="center" android:layout_margintop="10dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="111">  
  11. </textview></seekbar></textview></textview></linearlayout>  
마지막으로 이 뷰를 사용한 Activity 사용하 실 분들은 위의 클래스들을 만들어 놓고 이 Activity에 대해서만 수정하면 손쉽게 이용 할 수 잇다. VerticalSlidebarExampleActivity.Java
  1. package com.ememomo.tistory;  
  2.   
  3. import com.tokaracamara.android.verticalslidevar.R;  
  4.   
  5. import android.app.Activity;  
  6. import android.os.Bundle;  
  7. import android.widget.SeekBar;  
  8. import android.widget.TextView;  
  9. import android.widget.SeekBar.OnSeekBarChangeListener;  
  10.   
  11. public class VerticalSlidebarExampleActivity extends Activity {  
  12.       
  13.     TextView        txt_Progress;  
  14.     TextView        txt_HorizonProgress;  
  15.     VerticalSeekBar vertical_SeekBar;  
  16.     SeekBar         horizon_SeekBar;  
  17.     @Override  
  18.     public void onCreate(Bundle savedInstanceState) {  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.main);  
  21.   
  22.         txt_Progress        = (TextView)findViewById(R.id.progress);  
  23.         vertical_SeekBar    = (VerticalSeekBar)findViewById(R.id.vertical_seekbar);  
  24.         txt_HorizonProgress = (TextView)findViewById(R.id.horizon_progress);  
  25.         horizon_SeekBar     = (SeekBar)findViewById(R.id.horizontal_seekbar);  
  26.          
  27.         vertical_SeekBar.setMax(100);  
  28.         vertical_SeekBar.setProgress(30);  
  29.           
  30.         txt_Progress.setText("Progress is : " + vertical_SeekBar.getProgress());  
  31.         vertical_SeekBar.setOnSeekBarChangeListener(new VerticalSeekBar.OnSeekBarChangeListener() {  
  32.   
  33.             @Override  
  34.             public void onStopTrackingTouch(VerticalSeekBar seekBar) {  
  35.             }  
  36.   
  37.             @Override  
  38.             public void onStartTrackingTouch(VerticalSeekBar seekBar) {  
  39.                   
  40.             }  
  41.   
  42.             @Override  
  43.             public void onProgressChanged(VerticalSeekBar seekBar, int progress,  
  44.                     boolean fromUser) {  
  45.                   
  46.                 txt_Progress.setText("Progress is : " + vertical_SeekBar.getProgress());  
  47.             }  
  48.         });  
  49.           
  50.         horizon_SeekBar.setMax(100);  
  51.         horizon_SeekBar.setProgress(50);  
  52.         txt_HorizonProgress.setText("Progress is : " + horizon_SeekBar.getProgress());  
  53.           
  54.         horizon_SeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {  
  55.               
  56.             @Override  
  57.             public void onStopTrackingTouch(SeekBar arg0) {  
  58.                 // TODO Auto-generated method stub  
  59.                   
  60.             }  
  61.               
  62.             @Override  
  63.             public void onStartTrackingTouch(SeekBar arg0) {  
  64.                 // TODO Auto-generated method stub  
  65.                   
  66.             }  
  67.               
  68.             @Override  
  69.             public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {  
  70.                  txt_HorizonProgress.setText("Progress is : " + horizon_SeekBar.getProgress());  
  71.                   
  72.             }  
  73.         });  
  74.   
  75.     }  
  76. }  

세로 seekbar 관련 소스 and 파일