需求分析
这车载设置中,一般都会有音场设置,效果图如下:
就是要实现让如图所示的小球在指定区域内跟随手指移动,同时左边的数值跟随变化。当拖动左边的进度条时,小球也跟随变化,这里不做讲解,主要实现小球跟随手指的移动而移动。
案例实现
1.由于小球是在指定区域内移动,首先我们就需要获取指定区域的宽高,而需要获取控件的宽高,在 onCreate 是获取不到的,因为 View 组件布局要在 onResume 回调后完成,通常的做法是使用 getViewTreeObserver().addOnGlobalLayoutListener() 来获得宽度或者高度。OnGlobalLayoutListener 是 ViewTreeObserver 的内部类,当一个视图树的布局发生改变时,可以被 ViewTreeObserver 监听到,这是一个注册监听视图树的观察者 (observer),在视图树的全局事件改变时得到通知。ViewTreeObserver 不能直接实例化,而是通过 getViewTreeObserver() 获得。同时需要注意的是 OnGlobalLayoutListener 可能会被多次触发,因此在得到了高度之后,要将 OnGlobalLayoutListener 注销掉。代码如下:
mBallZone.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mBallZone.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mZoneHeight = mBallZone.getMeasuredHeight();
mZoneWidth = mBallZone.getMeasuredWidth();
Log.d(TAG, "onGlobalLayout: "+mZoneHeight+".."+mZoneWidth);
mPointX = mZoneWidth/2;
mPointY = mZoneHeight /2;
}
});
2.小球的移动范围是有边界控制的,小球离边界的距离相差一个半径,所以我们需要获取小球半径:
private void getImageOps(){
mSrcBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.setting_balance_centre);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.setting_balance_centre, opts);
opts.inSampleSize = 1;
opts.inJustDecodeBounds = false;
Bitmap mBitmap =BitmapFactory.decodeResource(getResources(), R.drawable.setting_balance_centre, opts);
mBallWidth =opts.outWidth;
mBallHeight =opts.outHeight;
}
3.获取到了指定移动范围和小球的半径参数后,接下来就是对手势监听,使得小球跟随手指移动。
class DrawBall extends View {
public DrawBall(Context context) {
super(context);
}
public DrawBall(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mSrcBitmap,mPointX-mBallWidth/2, mPointY-mBallHeight/2, null);
Log.d(TAG, "onDraw: mPointX::"+mPointX+"...mPointY::"+mPointY);
mIvHor.setY(mPointY);
mIvVer.setX(mPointX);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// mIsTouchBall = true;
mPointX = event.getX();
mPointY = event.getY();
isInvalidPoint();
mBall.invalidate();
return true;
}
}
/**
* 做边界判断,超过边界范围的,都默认等于边界值
*/
private void isInvalidPoint(){
if (mPointX-mBallWidth/2 <= 0)
mPointX = mBallWidth/2;
if (mPointY -mBallHeight/2<= 0)
mPointY = mBallHeight/2;
if (mPointX +mBallWidth/2>= mZoneWidth)
mPointX = mZoneWidth-mBallWidth/2;
if (mPointY +mBallHeight/2>= mZoneHeight)
mPointY = mZoneHeight-mBallHeight/2;
}
在代码中,将当前 onTouchEvent 方法返回 ture,表明这个 view 是要对当前手势操作进行捕获的,同时调用 mBall.invalidate() 刷新界面。
4.水平线与垂直线跟随手指的移动,当小球重绘时,同时改变两根线的位置。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mSrcBitmap,mPointX-mBallWidth/2, mPointY-mBallHeight/2, null);
Log.d(TAG, "onDraw: mPointX::"+mPointX+"...mPointY::"+mPointY);
mIvHor.setY(mPointY);
mIvVer.setX(mPointX);
}
5.使用方法
在 onCreate 中,创建自定义的小球 mBall,并将这个自定义的控件加入到父布局中:
mBall = new DrawBall(this);
mBallZone.addView(mBall);
这里列出的代码差不多都是伪代码,在实际使用中可能还需要调试,因为涉及到公司利益,就不贴全部代码,但相信,上述代码,基本能实现小球跟随手指移动而移动的功能了!
happy a nice day!