打造一个Android的遮罩层SeekBar

Home / Android MrLee 2015-8-3 4308

简介:所谓遮罩动画,就是利用遮罩的动画原理来实现的。可以看到的动画是遮罩层区域的内容。
说说这个实现原理: 原理其实很简单,只要了解android画笔怎么工作的就非常容易理解。 一:指定绘制图片或者层的大小(矩形-x,y,width,height) 二:指定刷新的区域(矩形-x,y,width,height)
例如一张100*100的图片,我们可以只让它绘制矩形为:0,0,30,30,在Android里面已经提供了这个函数:drawBitmap(bitmap, src, dst, paint) Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to draw. Note: if the paint contains a maskfilter that generates a mask which extends beyond the bitmap's original width/height (e.g. BlurMaskFilter), then the bitmap will be drawn as if it were in a Shader with CLAMP mode. Thus the color outside of the original width/height will be the edge color replicated.
This function ignores the density associated with the bitmap. This is because the source and destination rectangle coordinate spaces are in their respective densities, so must already have the appropriate scaling factor applied.
Parameters: bitmap The bitmap to be drawn src May be null. The subset of the bitmap to be drawn dst The rectangle that the bitmap will be scaled/translated to fit into paint May be null. The paint used to draw the bitmap 那么上面是绘制图片指定大小。再怎么设置屏幕刷新区域呢?Andriod画布类也提供了方法,invalidate(l, t, r, b) Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The coordinates of the dirty rect are relative to the view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate(). Parameters: l the left position of the dirty region t the top position of the dirty region r the right position of the dirty region b the bottom position of the dirty region
通过上面两个可以实现很多动画。不过这并不是我们要讲的重点,这只是告诉大家实现的原理。我们不需要这么复杂,因为Andrid已经封装好了函数,我们拿来用就行了!clipRect(l, t, r, b) Mark the area defined by the rect (l,t,r,b) as needing to be drawn. The coordinates of the dirty rect are relative to the view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate(). Parameters: l the left position of the dirty region t the top position of the dirty region r the right position of the dirty region b the bottom position of the dirty region
看下面的动态图片

seekbar

源码部分:
package com.lee.seekbar;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.ImageView;
@SuppressLint("ClickableViewAccessibility")
public class ArcSeekBar extends ImageView {
	Bitmap fullBitmap;
	Paint fullPaint;
	Bitmap buttonBitmap;
	Paint buttonPaint;
	// 859 419原始大小
	int ARC_START_X = 87;// 弧度起始x坐标
	int ARC_START_Y = 142;// 弧度起始y坐标
	// 可运行轨迹 半径
	float RUN_RADIUS = 375;
	float BITMAP_SCAL = 0;// 图片显示和真实比例
	// 图片中心坐标
	int PIC_X;
	// 按钮中心
	int BTN_CX;// 按钮宽的一半
	int BTN_CY;// 按钮高的一半
	// 裁剪区
	RectF rectF;// 遮罩坐标,大小
	Path path;// 遮罩路径
	float startAngle;// 运行起始角度
	final float sweepAngle = 180;// 半圆形遮罩层
	final int level = 16;// 级别
	int[] levelAreaX = new int[level];// x坐标活动区间
	float[] levelArea = new float[level];// 按钮活动弧度区间
	float offset_btn;// 按钮微调
	float minArc = 18;// 起始弧度 最小值 用PS测量可得,该值不受屏幕大小影响
	float maxArc = 157;// 结束弧度 最大值,该值不受屏幕大小影响
	SeekBarActionListener seekBarActionListener;
	boolean uniform;// 是否匀速
	public ArcSeekBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		TypedArray a = context.obtainStyledAttributes(attrs,
				R.styleable.ArcSeekBar);
		int resID = a.getResourceId(R.styleable.ArcSeekBar_fullbitmap, 0);
		fullBitmap = BitmapFactory.decodeResource(getResources(), resID);
		resID = a.getResourceId(R.styleable.ArcSeekBar_buttonbitmap, 0);
		buttonBitmap = BitmapFactory.decodeResource(getResources(), resID);
		int dstWidth = (int) (buttonBitmap.getWidth() * 1.5f);
		int dstHeight = (int) (buttonBitmap.getHeight() * 1.5f);
		buttonBitmap = Bitmap.createScaledBitmap(buttonBitmap, dstWidth,
				dstHeight, false);
		BTN_CX = buttonBitmap.getWidth() >> 1;
		BTN_CY = buttonBitmap.getHeight() >> 1;
		fullPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		buttonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
		offset_btn = getResources().getDimensionPixelSize(R.dimen.size_last);
		a.recycle();
		// 计算出当前屏幕显示图片放大或缩小的比例
		PIC_X = fullBitmap.getWidth() >> 1;
		BITMAP_SCAL = fullBitmap.getWidth() / 859f;
		// 轨迹
		ARC_START_X = (int) (ARC_START_X * BITMAP_SCAL);
		ARC_START_Y = (int) (ARC_START_Y * BITMAP_SCAL);
		RUN_RADIUS -= (BTN_CX >> 1);// 运行半径减去按钮半径
		RUN_RADIUS = RUN_RADIUS * BITMAP_SCAL;
		float offset = (maxArc - minArc) / (level - 1);// 每个区间的间隔的弧度
		int offsetX = ((int) RUN_RADIUS << 1) / (level - 1);
		for (int i = 0, j = levelArea.length - 1; i < levelArea.length; i++, j--) {
			levelArea[j] = minArc + offset * i;
			levelAreaX[i] = ARC_START_X + offsetX * i;
		}
		rectF = new RectF(0, -fullBitmap.getHeight(), fullBitmap.getWidth(),
				fullBitmap.getHeight());
		path = new Path();
		// 运动范围157~18
		// PS测试数据计算得出
		startAngle = getRotation(PIC_X, 0, ARC_START_X, ARC_START_Y) + 90;
		uniform = true;
	}
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		drawArc(canvas);
	}
	private void drawArc(Canvas canvas) {
		if (fullBitmap != null) {
			canvas.save();
			path.reset();
			path.addArc(rectF, startAngle, sweepAngle);
			path.close();
			canvas.clipPath(path);
			canvas.drawBitmap(fullBitmap, 0, 0, fullPaint);
			canvas.restore();
		}
		if (buttonBitmap != null) {
			canvas.save();
			float cx = (float) Math.cos(Math.toRadians(startAngle))
					* RUN_RADIUS + PIC_X;
			float cy = (float) Math.sin(Math.toRadians(startAngle))
					* RUN_RADIUS;
			canvas.rotate(startAngle - 90, cx, cy);
			float last_offset = nLastIndex == levelArea.length - 1 ? 2 : 0;// 调最后一个
			canvas.drawBitmap(buttonBitmap, cx - BTN_CX, cy - BTN_CY
					+ offset_btn + last_offset, buttonPaint);
			canvas.restore();
		}
	}
	float downX;
	int nIndex;
	int nLastIndex;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		if (uniform) {
			float x1 = event.getX();
			float y1 = event.getY();
			startAngle = getRotation(PIC_X, 0, x1, y1) + 90;
		}
		if (startAngle > maxArc)
			return true;
		if (startAngle < minArc)
			return true;
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downX = event.getX();
			break;
		case MotionEvent.ACTION_MOVE:
			float X = event.getX();
			if (X - downX > 0) {
				// 往右
				nIndex = getOffsetIndex(X);
				downX = X;
			} else {
				// 往左
				nIndex = getOffsetIndex(X);
				downX = X;
			}
			if (nIndex >= 0 && nIndex < levelArea.length) {
				if (!uniform)// 不是匀速
					startAngle = levelArea[nIndex];
				if (seekBarActionListener != null && nLastIndex != nIndex)
					seekBarActionListener.seek(nIndex);
				nLastIndex = nIndex;
			}
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			invalidate();
			break;
		}
		return super.onTouchEvent(event);
	}
	public void setnIndex(int nIndex) {
		this.nIndex = nIndex;
		startAngle = levelArea[nIndex];
		postInvalidate();
	}
	// 获取当前益所在的区间 0~level-1
	private int getOffsetIndex(float x) {
		for (int i = 0; i < levelAreaX.length; i++) {
			if (i < levelAreaX.length - 1) {
				if (x >= levelAreaX[i] && x <= levelAreaX[i + 1])
					return i;
			} else {
				if (x >= levelAreaX[i])
					return i;
			}
		}
		return -1;
	}
	private float getRotation(float x1, float y1, float x2, float y2) {
		float value = (float) Math.toDegrees(Math.atan2(y2 - y1, x2 - x1));
		return value - 90f;// 坐标系
	}
	public void setSeekBarActionListener(
			SeekBarActionListener seekBarActionListener) {
		this.seekBarActionListener = seekBarActionListener;
	}
	public boolean isUniform() {
		return uniform;
	}
	public void setUniform(boolean uniform) {
		this.uniform = uniform;
	}
	public interface SeekBarActionListener {
		public void seek(int index);
	}
}


normal

seleted

本文链接:https://www.it72.com/3937.htm

推荐阅读
最新回复 (0)
返回