SeekBar 垂直,进度点与thumb指示同步对齐

最近做项目,要用到垂直SeekBar,在网上找了一把,发现觉得的办法都是那么几个,弄下来实际测试了下,发现,是垂直了,可以拖动,但是,进度值与thumb只是图片无法同步对齐,要么有断层,要么超出了,要么触点偏移。所以针对网上一种找不到的,细调了下参数,完美实现了对齐,同时对调整中,对这个控件的一些逻辑了解,做个记录。

SeekBar 垂直,进度点与thumb指示同步对齐_第1张图片SeekBar 垂直,进度点与thumb指示同步对齐_第2张图片

第一张图片,进度背景比较暗,不明显。

先上完整代码

package com.example.testpb;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.AbsSeekBar;
import android.widget.SeekBar;

public class VerticalBar extends AbsSeekBar {
	private Drawable mThumb;
	private int height;
	private int width;

	public interface OnSeekBarChangeListener {
		void onProgressChanged(VerticalBar VerticalSeekBar, int progress, boolean fromUser);

		void onStartTrackingTouch(VerticalBar VerticalSeekBar);

		void onStopTrackingTouch(VerticalBar VerticalSeekBar);
	}

	private OnSeekBarChangeListener mOnSeekBarChangeListener;

	public VerticalBar(Context context) {
		this(context, null);
	}

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

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

	public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
		mOnSeekBarChangeListener = l;

	}

	void onStartTrackingTouch() {
		if (mOnSeekBarChangeListener != null) {
			mOnSeekBarChangeListener.onStartTrackingTouch(this);
		}
	}

	void onStopTrackingTouch() {
		if (mOnSeekBarChangeListener != null) {
			mOnSeekBarChangeListener.onStopTrackingTouch(this);
		}
	}

	void onProgressRefresh(float scale, boolean fromUser) {
		Drawable thumb = mThumb;
		if (thumb != null) {
			setThumbPos(getHeight(), thumb, scale, Integer.MIN_VALUE);
			invalidate();
		}
		if (mOnSeekBarChangeListener != null) {
			mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
		}
	}

	private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
		int available = w - getPaddingLeft() - getPaddingRight();
		System.out.println("--setThumbPos--available=" + available + "  scale=" + scale + "  getPaddingLeft=" + getPaddingLeft());

		int thumbWidth = thumb.getIntrinsicWidth();
		int thumbHeight = thumb.getIntrinsicHeight();
		// available -= thumbWidth;
		// available += getThumbOffset() / 2;
		System.out.println("--available=" + available);
		int thumbPos = (int) (scale * available);// 以实际进度值为参照
		int topBound, bottomBound;
		if (gap == Integer.MIN_VALUE) {
			Rect oldBounds = thumb.getBounds();
			topBound = oldBounds.top;
			bottomBound = oldBounds.bottom;
		} else {
			topBound = gap;
			bottomBound = gap + thumbHeight;
		}
		// 由于paddingLeft的宽度刚好为thumb指示图标的一半,所以,进度未0时,
		// thumb在整个widget的最左边,减去进度条的padding,thumb的中心圆点,刚好在进度条的0处
		thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
	}

	protected void onDraw(Canvas c) {
		System.out.println("--onDraw--height=" + height);
		c.rotate(-90);
		c.translate(-height, 0);
		super.onDraw(c);
	}

	protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		Drawable drawable = getThumb();
		System.out.println("--getThumb--Height=" + drawable.getIntrinsicHeight());
		System.out.println("--getThumb--Width=" + drawable.getIntrinsicWidth());
		width = drawable.getIntrinsicWidth();
		height = View.MeasureSpec.getSize(heightMeasureSpec);
		height = 200 + getPaddingLeft() * 2;// 高度为:实际seekbar的有效值(200)+左右Padding
		System.out.println("height=" + height + "width=" + width);
		this.setMeasuredDimension(width, height);
	}

	@Override
	public void setThumb(Drawable thumb) {
		mThumb = thumb;
		super.setThumb(thumb);
	}

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

	public boolean onTouchEvent(MotionEvent event) {
		if (!isEnabled()) {
			return false;
		}
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			setPressed(true);
			onStartTrackingTouch();
			trackTouchEvent(event);
			break;

		case MotionEvent.ACTION_MOVE:
			trackTouchEvent(event);
			attemptClaimDrag();
			break;

		case MotionEvent.ACTION_UP:
			trackTouchEvent(event);
			onStopTrackingTouch();
			setPressed(false);
			break;

		case MotionEvent.ACTION_CANCEL:
			onStopTrackingTouch();
			setPressed(false);
			break;
		}
		return true;
	}

	private void trackTouchEvent(MotionEvent event) {
		final int Height = getHeight();
		System.out.println("--trackTouchEvent-getHeight=" + getHeight() + "  getPaddingBottom=" + getPaddingBottom() + "  getPaddingLeft=" + getPaddingLeft());
		final int available = Height - getPaddingLeft() - getPaddingRight();
		System.out.println("--available=" + available);
		int Y = (int) event.getY();// 针对widget最边缘左上角
		float scale;
		float progress = 0;
		if (Y > Height - getPaddingRight()) {
			scale = 0.0f;
		} else if (Y < getPaddingTop()) {
			scale = 1.0f;
		} else {
			//Height- Y =总高度-触点=相对底部的高度
			scale = (float) (Height- Y - getPaddingLeft() ) / (float) available;
		}

		final int max = getMax();
		progress = scale * max;
		System.out.println("--scale=" + scale + "  progress=" + (int) progress);
		setProgress((int) progress);
	}

	private void attemptClaimDrag() {
		if (getParent() != null) {
			getParent().requestDisallowInterceptTouchEvent(true);
		}
	}

	public boolean dispatchKeyEvent(KeyEvent event) {
		if (event.getAction() == KeyEvent.ACTION_DOWN) {
			KeyEvent newEvent = null;
			switch (event.getKeyCode()) {
			case KeyEvent.KEYCODE_DPAD_UP:
				newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
				break;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN);
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_UP);
				break;
			default:
				newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, event.getKeyCode());
				break;
			}
			return newEvent.dispatch(this);
		}
		return false;
	}
}


继承AbsSeekBar,主要涉及4个方法的调整:

1.旋转画布,调整SeekBar的显示方向

	protected void onDraw(Canvas c) {
		System.out.println("--onDraw--height=" + height);
		c.rotate(-90);//逆时针旋转90°
		c.translate(-height, 0);//旋转中心点,在左下角
		super.onDraw(c);
	}
2.设置SeekBar的实际宽高,,注意,由于我们的SeekBar是旋转而来的,所以注意paddingLeft是针对原来水平的。

设置宽度为:thumb指示图片的高。(水平时候的高)

高度为:200(实际进度总值对应的点)+paddingLeft+paddingRight。(水平时候的宽)

protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		Drawable drawable = getThumb();
		System.out.println("--getThumb--Height=" + drawable.getIntrinsicHeight());
		System.out.println("--getThumb--Width=" + drawable.getIntrinsicWidth());
		width = drawable.getIntrinsicWidth();
		height = View.MeasureSpec.getSize(heightMeasureSpec);
		height = 200 + getPaddingLeft() * 2;// 高度为:实际seekbar的有效值(200)+左右Padding
		System.out.println("height=" + height + "width=" + width);
		this.setMeasuredDimension(width, height);
	}

3.滑块触点变化,处理。MAX对应available实际点数(200)。更改进度值

private void trackTouchEvent(MotionEvent event) {
		final int Height = getHeight();
		System.out.println("--trackTouchEvent-getHeight=" + getHeight() + "  getPaddingBottom=" + getPaddingBottom() + "  getPaddingLeft=" + getPaddingLeft());
		final int available = Height - getPaddingLeft() - getPaddingRight();
		System.out.println("--available=" + available);
		int Y = (int) event.getY();// 针对widget最边缘左上角
		float scale;
		float progress = 0;
		if (Y > Height - getPaddingRight()) {//触点在进度值最下面
			scale = 0.0f;
		} else if (Y < getPaddingLeft()) {//触点在进度值最上面
			scale = 1.0f;
		} else {
			//Height- Y =总高度-触点=相对底部的高度
			scale = (float) (Height- Y - getPaddingLeft() ) / (float) available;
		}
		final int max = getMax();
		progress = scale * max;
		System.out.println("--scale=" + scale + "  progress=" + (int) progress);
		setProgress((int) progress);
	}

4.进度值改变,触发thumb位置的更改,获得当前进度值+paddingLeft=thumb中心圆点在widget的位置。所以,thumb左边缘与widget的距离=当前的进度值。

thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);

设置绘制thumb的区域大小,针对widget的最左边。

private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
		int available = w - getPaddingLeft() - getPaddingRight();
		System.out.println("--setThumbPos--available=" + available + "  scale=" + scale + "  getPaddingLeft=" + getPaddingLeft());

		int thumbWidth = thumb.getIntrinsicWidth();
		int thumbHeight = thumb.getIntrinsicHeight();
		// available -= thumbWidth;
		// available += getThumbOffset() / 2;
		System.out.println("--available=" + available);
		int thumbPos = (int) (scale * available);// 以实际进度值为参照
		int topBound, bottomBound;
		if (gap == Integer.MIN_VALUE) {
			Rect oldBounds = thumb.getBounds();
			topBound = oldBounds.top;
			bottomBound = oldBounds.bottom;
		} else {
			topBound = gap;
			bottomBound = gap + thumbHeight;
		}
		// 由于paddingLeft的宽度刚好为thumb指示图标的一半,所以,进度未0时,
		// thumb在整个widget的最左边,减去进度条的padding,thumb的中心圆点,刚好在进度条的0处
		thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
	}



所以,这个控件的距离,要注意的是,他对应的参照点是谁,分清楚了,就好说了。

对于ProgressBar垂直,有一种,更简单的实现:

设置

<item name="android:progressDrawable">@drawable/progress_vertical</item>
progress_vertical.xml如下,设置progress的clip clipOrientation="vertical"垂直,android:gravity="bottom"  底部对齐
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2008 The Android Open Source 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.

-->

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:id="@android:id/background">
         <clip
            android:clipOrientation="vertical"
            android:gravity="bottom" >
        <shape>
            <corners android:radius="5dip" />

            <gradient
                android:angle="270"
                android:centerColor="#ff5a5d5a"
                android:centerX="0.75"
                android:endColor="#ff747674"
                android:startColor="#ff9d9e9d" />
        </shape>
          </clip>
    </item>
    <item android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom" >
            <shape>
                <corners android:radius="5dip" />

                <gradient
                    android:angle="90"
                    android:centerColor="#ffffb600"
                    android:centerX="0.75"
                    android:endColor="#ffffcb00"
                    android:startColor="#ffffd300" />
            </shape>
        </clip>
    </item>
</layer-list>

需要完整代码的码的,请到这里下载 http://download.csdn.net/detail/luohaowang320/6544245


你可能感兴趣的:(SeekBar 垂直,进度点与thumb指示同步对齐)