Android 自定义UI-垂直方向的SeekBar

版本:2.0

日期:2014.4.14 2014.5.7
版权:© 2014 kince 转载注明出处
  
方式一

  系统自带的SeekBar样式是水平的,如果需求一个垂直方向的效果就需要自定义了。原理很简单,即定义一个类继承于SeekBar,并在OnDraw方法里面旋转一下视图。

代码如下:

package android.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

public class VerticalSeekBar extends SeekBar {

    public VerticalSeekBar(Context context) {
        super(context);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public VerticalSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(h, w, oldh, oldw);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(heightMeasureSpec, widthMeasureSpec);
        setMeasuredDimension(getMeasuredHeight(), getMeasuredWidth());
    }

    protected void onDraw(Canvas c) {
    	//将SeekBar转转90度
        c.rotate(-90);
        //将旋转后的视图移动回来
        c.translate(-getHeight(),0);
        Log.i("getHeight()",getHeight()+"");
        super.onDraw(c);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
            	int i=0;
            	//获取滑动的距离
            	i=getMax() - (int) (getMax() * event.getY() / getHeight());
            	//设置进度
                setProgress(i);
                Log.i("Progress",getProgress()+"");
                //每次拖动SeekBar都会调用
                onSizeChanged(getWidth(), getHeight(), 0, 0);
                Log.i("getWidth()",getWidth()+"");
                Log.i("getHeight()",getHeight()+"");
                break;

            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }
    
}
  xml布局文件:




    

    

    

    

   Android 自定义UI-垂直方向的SeekBar_第1张图片
方式二
  还有一种方式就是对系统的ProgressBar进行修改,代码如下:
/*
 *              Copyright (C) 2011 The MusicMod Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *            http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.joggingTunes.android.activities;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewParent;
import android.widget.RemoteViews.RemoteView;
import android.os.Parcel;
import android.os.Parcelable;

@RemoteView
public class VerticalProgressBar extends View {
	private static final int MAX_LEVEL = 10000;

	int mMinWidth;
	int mMaxWidth;
	int mMinHeight;
	int mMaxHeight;

	private int mProgress;
	private int mSecondaryProgress;
	private int mMax;

	private Drawable mProgressDrawable;
	private Drawable mCurrentDrawable;
	Bitmap mSampleTile;
	private boolean mNoInvalidate;
	private RefreshProgressRunnable mRefreshProgressRunnable;
	private long mUiThreadId;

	private boolean mInDrawing;

	protected int mScrollX;
	protected int mScrollY;
	protected int mPaddingLeft;
	protected int mPaddingRight;
	protected int mPaddingTop;
	protected int mPaddingBottom;
	protected ViewParent mParent;

	/**
	 * Create a new progress bar with range 0...100 and initial progress of 0.
	 * 
	 * @param context
	 *            the application environment
	 */
	public VerticalProgressBar(Context context) {
		this(context, null);
	}

	public VerticalProgressBar(Context context, AttributeSet attrs) {
		this(context, attrs, android.R.attr.progressBarStyle);
	}

	public VerticalProgressBar(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mUiThreadId = Thread.currentThread().getId();
		initProgressBar();

		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.ProgressBar, defStyle, 0);

		mNoInvalidate = true;

		Drawable drawable = a
				.getDrawable(R.styleable.ProgressBar_android_progressDrawable);
		if (drawable != null) {
			drawable = tileify(drawable, false);
			// Calling this method can set mMaxHeight, make sure the
			// corresponding
			// XML attribute for mMaxHeight is read after calling this method
			setProgressDrawable(drawable);
		}

		mMinWidth = a.getDimensionPixelSize(
				R.styleable.ProgressBar_android_minWidth, mMinWidth);
		mMaxWidth = a.getDimensionPixelSize(
				R.styleable.ProgressBar_android_maxWidth, mMaxWidth);
		mMinHeight = a.getDimensionPixelSize(
				R.styleable.ProgressBar_android_minHeight, mMinHeight);
		mMaxHeight = a.getDimensionPixelSize(
				R.styleable.ProgressBar_android_maxHeight, mMaxHeight);

		setMax(a.getInt(R.styleable.ProgressBar_android_max, mMax));

		setProgress(a.getInt(R.styleable.ProgressBar_android_progress,
				mProgress));

		setSecondaryProgress(a.getInt(
				R.styleable.ProgressBar_android_secondaryProgress,
				mSecondaryProgress));

		mNoInvalidate = false;

		a.recycle();
	}

	/**
	 * Converts a drawable to a tiled version of itself. It will recursively
	 * traverse layer and state list drawables.
	 */
	private Drawable tileify(Drawable drawable, boolean clip) {

		if (drawable instanceof LayerDrawable) {
			LayerDrawable background = (LayerDrawable) drawable;
			final int N = background.getNumberOfLayers();
			Drawable[] outDrawables = new Drawable[N];

			for (int i = 0; i < N; i++) {
				int id = background.getId(i);
				outDrawables[i] = tileify(
						background.getDrawable(i),
						(id == android.R.id.progress || id == android.R.id.secondaryProgress));
			}

			LayerDrawable newBg = new LayerDrawable(outDrawables);

			for (int i = 0; i < N; i++) {
				newBg.setId(i, background.getId(i));
			}

			return newBg;

		} else if (drawable instanceof StateListDrawable) {
			StateListDrawable out = new StateListDrawable();
			return out;

		} else if (drawable instanceof BitmapDrawable) {
			final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
			if (mSampleTile == null) {
				mSampleTile = tileBitmap;
			}

			final ShapeDrawable shapeDrawable = new ShapeDrawable(
					getDrawableShape());
			return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
					ClipDrawable.HORIZONTAL) : shapeDrawable;
		}

		return drawable;
	}

	Shape getDrawableShape() {
		final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 };
		return new RoundRectShape(roundedCorners, null, null);
	}

	/**
	 * 

* Initialize the progress bar's default values: *

*
    *
  • progress = 0
  • *
  • max = 100
  • *
*/ private void initProgressBar() { mMax = 100; mProgress = 0; mSecondaryProgress = 0; mMinWidth = 24; mMaxWidth = 48; mMinHeight = 24; mMaxHeight = 48; } /** *

* Get the drawable used to draw the progress bar in progress mode. *

* * @return a {@link android.graphics.drawable.Drawable} instance * * @see #setProgressDrawable(android.graphics.drawable.Drawable) */ public Drawable getProgressDrawable() { return mProgressDrawable; } /** *

* Define the drawable used to draw the progress bar in progress mode. *

* * @param d * the new drawable * * @see #getProgressDrawable() */ public void setProgressDrawable(Drawable d) { if (d != null) { d.setCallback(this); // Make sure the ProgressBar is always tall enough int drawableHeight = d.getMinimumHeight(); if (mMaxHeight < drawableHeight) { mMaxHeight = drawableHeight; requestLayout(); } } mProgressDrawable = d; mCurrentDrawable = d; postInvalidate(); } /** * @return The drawable currently used to draw the progress bar */ Drawable getCurrentDrawable() { return mCurrentDrawable; } @Override protected boolean verifyDrawable(Drawable who) { return who == mProgressDrawable || super.verifyDrawable(who); } @Override public void postInvalidate() { if (!mNoInvalidate) { super.postInvalidate(); } } private class RefreshProgressRunnable implements Runnable { private int mId; private int mProgress; private boolean mFromUser; RefreshProgressRunnable(int id, int progress, boolean fromUser) { mId = id; mProgress = progress; mFromUser = fromUser; } public void run() { doRefreshProgress(mId, mProgress, mFromUser); // Put ourselves back in the cache when we are done mRefreshProgressRunnable = this; } public void setup(int id, int progress, boolean fromUser) { mId = id; mProgress = progress; mFromUser = fromUser; } } private synchronized void doRefreshProgress(int id, int progress, boolean fromUser) { float scale = mMax > 0 ? (float) progress / (float) mMax : 0; final Drawable d = mCurrentDrawable; if (d != null) { Drawable progressDrawable = null; if (d instanceof LayerDrawable) { progressDrawable = ((LayerDrawable) d) .findDrawableByLayerId(id); } final int level = (int) (scale * MAX_LEVEL); (progressDrawable != null ? progressDrawable : d).setLevel(level); } else { invalidate(); } if (id == android.R.id.progress) { onProgressRefresh(scale, fromUser); } } void onProgressRefresh(float scale, boolean fromUser) { } private synchronized void refreshProgress(int id, int progress, boolean fromUser) { if (mUiThreadId == Thread.currentThread().getId()) { doRefreshProgress(id, progress, fromUser); } else { RefreshProgressRunnable r; if (mRefreshProgressRunnable != null) { // Use cached RefreshProgressRunnable if available r = mRefreshProgressRunnable; // Uncache it mRefreshProgressRunnable = null; r.setup(id, progress, fromUser); } else { // Make a new one r = new RefreshProgressRunnable(id, progress, fromUser); } post(r); } } /** *

* Set the current progress to the specified value. *

* * @param progress * the new progress, between 0 and {@link #getMax()} * * @see #getProgress() * @see #incrementProgressBy(int) */ public synchronized void setProgress(int progress) { setProgress(progress, false); } synchronized void setProgress(int progress, boolean fromUser) { if (progress < 0) { progress = 0; } if (progress > mMax) { progress = mMax; } if (progress != mProgress) { mProgress = progress; refreshProgress(android.R.id.progress, mProgress, fromUser); } } /** *

* Set the current secondary progress to the specified value. *

* * @param secondaryProgress * the new secondary progress, between 0 and {@link #getMax()} * @see #getSecondaryProgress() * @see #incrementSecondaryProgressBy(int) */ public synchronized void setSecondaryProgress(int secondaryProgress) { if (secondaryProgress < 0) { secondaryProgress = 0; } if (secondaryProgress > mMax) { secondaryProgress = mMax; } if (secondaryProgress != mSecondaryProgress) { mSecondaryProgress = secondaryProgress; refreshProgress(android.R.id.secondaryProgress, mSecondaryProgress, false); } } /** *

* Get the progress bar's current level of progress. *

* * @return the current progress, between 0 and {@link #getMax()} * * @see #setProgress(int) * @see #setMax(int) * @see #getMax() */ @ViewDebug.ExportedProperty public synchronized int getProgress() { return mProgress; } /** *

* Get the progress bar's current level of secondary progress. *

* * @return the current secondary progress, between 0 and {@link #getMax()} * * @see #setSecondaryProgress(int) * @see #setMax(int) * @see #getMax() */ @ViewDebug.ExportedProperty public synchronized int getSecondaryProgress() { return mSecondaryProgress; } /** *

* Return the upper limit of this progress bar's range. *

* * @return a positive integer * * @see #setMax(int) * @see #getProgress() * @see #getSecondaryProgress() */ @ViewDebug.ExportedProperty public synchronized int getMax() { return mMax; } /** *

* Set the range of the progress bar to 0...max. *

* * @param max * the upper range of this progress bar * * @see #getMax() * @see #setProgress(int) * @see #setSecondaryProgress(int) */ public synchronized void setMax(int max) { if (max < 0) { max = 0; } if (max != mMax) { mMax = max; postInvalidate(); if (mProgress > max) { mProgress = max; refreshProgress(android.R.id.progress, mProgress, false); } } } /** *

* Increase the progress bar's progress by the specified amount. *

* * @param diff * the amount by which the progress must be increased * * @see #setProgress(int) */ public synchronized final void incrementProgressBy(int diff) { setProgress(mProgress + diff); } /** *

* Increase the progress bar's secondary progress by the specified amount. *

* * @param diff * the amount by which the secondary progress must be increased * * @see #setSecondaryProgress(int) */ public synchronized final void incrementSecondaryProgressBy(int diff) { setSecondaryProgress(mSecondaryProgress + diff); } @Override public void setVisibility(int v) { if (getVisibility() != v) { super.setVisibility(v); } } @Override public void invalidateDrawable(Drawable dr) { if (!mInDrawing) { if (verifyDrawable(dr)) { final Rect dirty = dr.getBounds(); final int scrollX = mScrollX + mPaddingLeft; final int scrollY = mScrollY + mPaddingTop; invalidate(dirty.left + scrollX, dirty.top + scrollY, dirty.right + scrollX, dirty.bottom + scrollY); } else { super.invalidateDrawable(dr); } } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { // onDraw will translate the canvas so we draw starting at 0,0 int right = w - mPaddingRight - mPaddingLeft; int bottom = h - mPaddingBottom - mPaddingTop; if (mProgressDrawable != null) { mProgressDrawable.setBounds(0, 0, right, bottom); } } @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); Drawable d = mCurrentDrawable; if (d != null) { // Translate canvas so a indeterminate circular progress bar with // padding // rotates properly in its animation canvas.save(); canvas.translate(mPaddingLeft, mPaddingTop); d.draw(canvas); canvas.restore(); } } @Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable d = mCurrentDrawable; int dw = 0; int dh = 0; if (d != null) { dw = Math .max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth())); dh = Math.max(mMinHeight, Math.min(mMaxHeight, d .getIntrinsicHeight())); } dw += mPaddingLeft + mPaddingRight; dh += mPaddingTop + mPaddingBottom; setMeasuredDimension(resolveSize(dw, widthMeasureSpec), resolveSize(dh, heightMeasureSpec)); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); int[] state = getDrawableState(); if (mProgressDrawable != null && mProgressDrawable.isStateful()) { mProgressDrawable.setState(state); } } static class SavedState extends BaseSavedState { int progress; int secondaryProgress; /** * Constructor called from {@link ProgressBar#onSaveInstanceState()} */ SavedState(Parcelable superState) { super(superState); } /** * Constructor called from {@link #CREATOR} */ private SavedState(Parcel in) { super(in); progress = in.readInt(); secondaryProgress = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(progress); out.writeInt(secondaryProgress); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } @Override public Parcelable onSaveInstanceState() { // Force our ancestor class to save its state Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.progress = mProgress; ss.secondaryProgress = mSecondaryProgress; return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setProgress(ss.progress); setSecondaryProgress(ss.secondaryProgress); } }

方式三
   还是从源码入手,找到设置ProgressBar样式的progress_horizontal.xml文件,
 
         
            
                 
                 
            
         
     
  为什么shape外面要包一层clip呢,官方文档解释是clipdrawable是可以自我复制的,来看看定义
 
  android:clipOrientation有两个属性,默认为horizontal,android:gravity有两个属性,默认为left。那我们试试改成vertical和bottom会有什么效果,新建一个progress_vertical.xml,把源码progress_horizontal.xml的内容复制过来,这里去掉了secondaryProgress,修改了clip,shape的渐变中心centerY改为centerX
 
         
             
                 
               
            
        
     
  布局中android:progressDrawable="@drawable/progress_vertical",ok,搞定。
  代码下载(方式一代码)

  推荐博文: Android Canvas编程:对rotate()和translate()两个方法的研究

你可能感兴趣的:(Android,Android自定义UI)