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布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:background="@android:color/white"
    android:orientation="horizontal" >

    <android.widget.VerticalSeekBar_Reverse
        android:id="@+id/seekbar_reverse"
        android:layout_width="wrap_content"
        android:layout_height="450dip"
        android:layout_marginRight="30dip" />

    <TextView
        android:id="@+id/reverse_sb_progresstext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/seekbar_reverse"
        android:gravity="center" />

    <android.widget.VerticalSeekBar
        android:id="@+id/vertical_Seekbar"
        android:layout_width="wrap_content"
        android:layout_height="450dip"
        android:layout_toRightOf="@+id/seekbar_reverse" />

    <TextView
        android:id="@+id/vertical_sb_progresstext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/vertical_Seekbar"
        android:layout_toRightOf="@+id/seekbar_reverse"
        android:gravity="center" />

</RelativeLayout>
  
方式二
  还有一种方式就是对系统的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);
	}

	/**
	 * <p>
	 * Initialize the progress bar's default values:
	 * </p>
	 * <ul>
	 * <li>progress = 0</li>
	 * <li>max = 100</li>
	 * </ul>
	 */
	private void initProgressBar() {
		mMax = 100;
		mProgress = 0;
		mSecondaryProgress = 0;
		mMinWidth = 24;
		mMaxWidth = 48;
		mMinHeight = 24;
		mMaxHeight = 48;
	}

	/**
	 * <p>
	 * Get the drawable used to draw the progress bar in progress mode.
	 * </p>
	 * 
	 * @return a {@link android.graphics.drawable.Drawable} instance
	 * 
	 * @see #setProgressDrawable(android.graphics.drawable.Drawable)
	 */
	public Drawable getProgressDrawable() {
		return mProgressDrawable;
	}

	/**
	 * <p>
	 * Define the drawable used to draw the progress bar in progress mode.
	 * </p>
	 * 
	 * @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);
		}
	}

	/**
	 * <p>
	 * Set the current progress to the specified value.
	 * </p>
	 * 
	 * @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);
		}
	}

	/**
	 * <p>
	 * Set the current secondary progress to the specified value.
	 * </p>
	 * 
	 * @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);
		}
	}

	/**
	 * <p>
	 * Get the progress bar's current level of progress.
	 * </p>
	 * 
	 * @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;
	}

	/**
	 * <p>
	 * Get the progress bar's current level of secondary progress.
	 * </p>
	 * 
	 * @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;
	}

	/**
	 * <p>
	 * Return the upper limit of this progress bar's range.
	 * </p>
	 * 
	 * @return a positive integer
	 * 
	 * @see #setMax(int)
	 * @see #getProgress()
	 * @see #getSecondaryProgress()
	 */
	@ViewDebug.ExportedProperty
	public synchronized int getMax() {
		return mMax;
	}

	/**
	 * <p>
	 * Set the range of the progress bar to 0...<tt>max</tt>.
	 * </p>
	 * 
	 * @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);
			}
		}
	}

	/**
	 * <p>
	 * Increase the progress bar's progress by the specified amount.
	 * </p>
	 * 
	 * @param diff
	 *            the amount by which the progress must be increased
	 * 
	 * @see #setProgress(int)
	 */
	public synchronized final void incrementProgressBy(int diff) {
		setProgress(mProgress + diff);
	}

	/**
	 * <p>
	 * Increase the progress bar's secondary progress by the specified amount.
	 * </p>
	 * 
	 * @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<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
			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文件,
 <item android:id="@android:id/progress">
         <clip>
            <shape>
                 <corners android:radius="5dip" />
                 <gradient
                        android:startColor="#ffffd300"
                         android:centerColor="#ffffb600"
                        android:centerY="0.75"
                        android:endColor="#ffffcb00"
                        android:angle="270"
                 />
            </shape>
         </clip>
     </item>
  为什么shape外面要包一层clip呢,官方文档解释是clipdrawable是可以自我复制的,来看看定义
 <?xml version="1.0" encoding="utf-8"?>
<clip
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/drawable_resource"
     android:clipOrientation=["horizontal" | "vertical"]
    android:gravity=["top" | "bottom" | "left" | "right" | "center_vertical" |
                     "fill_vertical" | "center_horizontal" | "fill_horizontal" |
                     "center" | "fill" | "clip_vertical" | "clip_horizontal"] />
  android:clipOrientation有两个属性,默认为horizontal,android:gravity有两个属性,默认为left。那我们试试改成vertical和bottom会有什么效果,新建一个progress_vertical.xml,把源码progress_horizontal.xml的内容复制过来,这里去掉了secondaryProgress,修改了clip,shape的渐变中心centerY改为centerX
 <item android:id="@android:id/progress">
         <clip
             android:clipOrientation="vertical"
             android:gravity = "bottom">
             <shape>
                 <corners android:radius="5dip" />
               <gradient
                         android:startColor="#ffffd300"
                        android:centerColor="#ffffb600"
                         android:centerX="0.75"
                         android:endColor="#ffffcb00"
                         android:angle="90"
                 />
            </shape>
        </clip>
     </item>
  布局中android:progressDrawable="@drawable/progress_vertical",ok,搞定。
  代码下载(方式一代码)

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

  • 13
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 23
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值