如图最近项目要做一个剪切音乐的控件,于是打算手写一个。
先分析一下,就是需要两个首尾的拉杆,开始的不能超过结束的,开始的不能低于起点,结束的不能低于开始的也不能高于终点。
package com.lewanjia.dancelog.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.lewanjia.dancelog.R;
import com.lewanjia.dancelog.utils.LogUtils;
/**
* @autor lzj
*/
public class CutMusicLineSeekBar extends View {
private Paint circlePaint;//
private Paint circlePaintOver;//
private Paint playPaint;
private float mHeight;
private float mWidth;
//x轴的原点坐标
private int xOri;
//y轴的原点坐标
private int yOri;
private Context mContext;
private Bitmap dotBitmapStart;
private Bitmap dotBitmapEnd;
private Drawable dotDrawableEnd;
private Drawable dotDrawableStart;
private float dotRadius;
private float circleB = 0;//半径
private final static int VAULE_3 = 3;
private final static int VAULE_4 = 4;
private float markPointX = 0;
private float markPointY = 0;
private float markPointXEnd = 0;
private float markPointYEnd = 0;
private float markPalyX = 0;
private int maxProgress = 100;
private float pointWidth;
private float pointHeight;
private float startProcess = 0;
private float endProcess = 100;
private float palyProcess= 0;
public CutMusicLineSeekBar(Context context) {
this(context, null);
}
public CutMusicLineSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CutMusicLineSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView(context, attrs, defStyleAttr);
}
private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CutMusicCircleSeekBar, defStyleAttr, 0);
int count = array.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = array.getIndex(i);
switch (attr) {
}
}
array.recycle();
}
/**
* 初始化画笔
*/
private void init() {
circlePaint = new Paint();
circlePaint.setColor(Color.parseColor("#a4a4a4"));
circlePaint.setAntiAlias(true);
circlePaint.setStyle(Paint.Style.STROKE);// 设置中空的样式
circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
circlePaint.setStrokeWidth(dip2px(mContext, VAULE_4));
circlePaintOver = new Paint();
circlePaintOver.setColor(Color.parseColor("#33000000"));
circlePaintOver.setAntiAlias(true);
circlePaintOver.setStyle(Paint.Style.FILL);// 设置中空的样式
circlePaintOver.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
circlePaintOver.setStrokeWidth(dip2px(mContext, VAULE_4));
playPaint = new Paint();
playPaint.setColor(Color.parseColor("#FFAA00"));
playPaint.setAntiAlias(true);
playPaint.setStyle(Paint.Style.FILL);// 设置中空的样式
playPaint.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
playPaint.setStrokeWidth(dip2px(mContext, 1));
dotDrawableEnd = mContext.getResources().getDrawable(R.mipmap.image_cut_music_right);
dotDrawableStart = mContext.getResources().getDrawable(R.mipmap.image_cut_music_left);
if (dotDrawableStart != null) {
dotBitmapStart = ((BitmapDrawable) dotDrawableStart).getBitmap();
dotBitmapEnd = ((BitmapDrawable) dotDrawableEnd).getBitmap();
dotRadius = Math.max(dotRadius, Math.max(dotBitmapStart.getWidth() / 2, dotBitmapStart.getHeight() / 2));
circleB = getWidth() / 2 - dip2px(mContext, VAULE_3) - dotBitmapStart.getWidth() / 4;
pointWidth = (float) dotBitmapEnd.getWidth();
pointHeight = dotBitmapEnd.getHeight();
}
dotRadius = dotBitmapStart.getWidth() / 2;
markPointXEnd = mWidth - pointWidth;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width;
int height;
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = widthSize * 1 / 2;
}
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = heightSize * 1 / 2;
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mHeight = getHeight();
mWidth = getWidth();
xOri = getWidth() / 2;
yOri = getHeight() / 2;
markPointY = mHeight / 2;
markPointYEnd = mHeight / 2;
init();
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawBitmpStart(canvas);
canvas.save();
}
float lastx;
float lasty;
boolean isOntouch = false;
int moveFirst = 0;//0start 1end 2paly
@Override
public boolean onTouchEvent(MotionEvent event) {
isOntouch = true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastx = event.getX();
lasty = event.getY();
double fisrt = distance(lastx, markPointX);
double second = distance(lastx, markPointXEnd);
double palyLine = distance(lastx, markPalyX);
if (fisrt<=second&&fisrt<=palyLine){
moveFirst = 0;
}else if (second<=fisrt&&second<=palyLine) {
moveFirst = 1;
}else if (palyLine<fisrt&&palyLine<=second){
moveFirst = 2;
}
moved(lastx, lasty, false, event, moveFirst);
return true;
case MotionEvent.ACTION_MOVE:
lastx = event.getX();
lasty = event.getY();
moved(lastx, lasty, false, event, moveFirst);
Log.e("", "");
return true;
case MotionEvent.ACTION_UP:
if (moveFirst==2){
if (onProcessChangeLister!=null){
onProcessChangeLister.changePaly(getPalyProgress());
}
}
return true;
// case MotionEvent.ACTION_CANCEL:
// f (moveFirst==2){
// if (onProcessChangeLister!=null){
// onProcessChangeLister.changePaly(getPalyProgress());
// }
// }
// return true;
}
return false;
}
private float distance(float x1, float x2) {
return (float) Math.abs(x1 - x2);
}
public int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* @param canvas
*/
private void drawBitmpStart(Canvas canvas) {
canvas.drawBitmap(dotBitmapStart, markPointX, 0, circlePaint);
canvas.drawBitmap(dotBitmapEnd, markPointXEnd, 0, circlePaint);
// circlePaintOver
RectF rectF1 = new RectF();
rectF1.left = 0;
rectF1.right = markPointX;
rectF1.top = dip2px(mContext, VAULE_4);
rectF1.bottom = mHeight - dip2px(mContext, VAULE_4);
RectF rectF2 = new RectF();
rectF2.left = markPointXEnd + pointWidth;
rectF2.right = mWidth;
rectF2.top = dip2px(mContext, VAULE_4);
rectF2.bottom = mHeight - dip2px(mContext, VAULE_4);
RectF rectF3 = new RectF();
rectF3.left = markPalyX + pointWidth;
rectF3.right = markPalyX + pointWidth + dip2px(mContext, 2);
rectF3.top = dip2px(mContext, VAULE_4);
rectF3.bottom = mHeight - dip2px(mContext, VAULE_4);
canvas.drawRect(rectF1, circlePaintOver);
canvas.drawRect(rectF2, circlePaintOver);
canvas.drawRect(rectF3, playPaint);
}
public void setPalyProgress(float progress){
this.palyProcess = progress;
markPalyX = (mWidth-pointWidth*2f)* progress/100f;
invalidate();
}
public float getPalyProgress(){
palyProcess =markPalyX/(mWidth-pointWidth*2f)*100f;
LogUtils.E("456","palyProcess=="+palyProcess);
return palyProcess>99.6?100:palyProcess;
}
public float getStartProgress(){
startProcess = markPointX/(mWidth-pointWidth*2f)*100f;
return startProcess>99.6?100:startProcess;
}
public float getEndProgress(){
endProcess = (( markPointXEnd - pointWidth)/(mWidth-pointWidth*2f)*100f);
return endProcess>99.6?100:endProcess;
}
public void setStartProgress(float progress){
this.startProcess = progress;
markPointX = (mWidth-pointWidth*2f)* progress/100f;
invalidate();
LogUtils.E("456","startProcess=="+startProcess);
if (onProcessChangeLister != null) {
onProcessChangeLister.changeStart(startProcess);
onProcessChangeLister.changeEnd(endProcess);
}
}
public void setEndProgress(float progress){
this.endProcess = progress;
markPointXEnd = (mWidth-pointWidth*2f)* progress/100f+pointWidth;
invalidate();
LogUtils.E("456","endProcess=="+endProcess);
if (onProcessChangeLister != null) {
onProcessChangeLister.changeStart(startProcess);
onProcessChangeLister.changeEnd(endProcess);
}
}
private void moved(float x, float y, boolean up, MotionEvent event, int moveFirst) {
if (x < 0 || x > mWidth - pointWidth)
return;
if (moveFirst==0) {
if (x >= markPointXEnd - pointWidth)
return;
markPointX = x;
if (markPalyX < markPointX) {
markPalyX = markPointX;
}
} else if (moveFirst==1){
if (x <= markPointX + pointWidth)
return;
markPointXEnd = x;
// if (markPalyX > markPointXEnd-pointWidth) {
// markPalyX = markPointXEnd-pointWidth;
// }
}else {
if (x >= mWidth - pointWidth*2)
return;
markPalyX= x;
}
if (onProcessChangeLister != null) {
onProcessChangeLister.changeStart(getStartProgress());
onProcessChangeLister.changeEnd(getEndProgress());
}
invalidate();
}
private OnProcessChangeLister onProcessChangeLister;
public interface OnProcessChangeLister {
void changeStart(float process);
void changeEnd(float preocess);
void changePaly(float preocess);
}
public void setOnProcessChangeLister(OnProcessChangeLister onProcessChangeLister) {
this.onProcessChangeLister = onProcessChangeLister;
}
public int getMaxProgress() {
return maxProgress;
}
}
代码看起来也不复杂,只要理清逻辑就很简单。如果还需要音乐轨道的波浪图,可以评论