可添加浮动bubble的seekbar
可添加浮动bubble的seekbar
Android 其它控件
共1Star
详细介绍
SeekBarDemoProject
版本1
继承seekbar,在seekbar中,拓展绘制范围;
在seekbar中,强行绘制了bubble
- 结果: 不够优雅;绘制占用的无用区域过多;
- 结论: 应该让绘制区域局限于仅仅需要的大小;
版本2
继承seekbar;
bubble在外部自定义后,利用传参的形式,作为外部的组件传递进来;
利用WindowManager控制其显示的位置;
- 步骤1: 在onMeasure中,对各种参数进行初始化;
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = getMeasuredWidth();
mHeight = getMeasuredHeight();
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mLayoutParams = new WindowManager.LayoutParams();
mViewBubble = (ViewGroup) mLayoutInflater.inflate(mBubbleLayoutId, (ViewGroup)getParent(), false);
setBubbleTextView(mBubbleTvId);
ViewGroup.LayoutParams bubbleLayoutParams = mViewBubble.getLayoutParams();
mBubbleWidth = bubbleLayoutParams.width;
mBubbleHeight = bubbleLayoutParams.height;
mLayoutParams.gravity= Gravity.LEFT | Gravity.TOP;
mLayoutParams.width = (int) mBubbleWidth;
mLayoutParams.height = (int) mBubbleHeight;
mLayoutParams.format = PixelFormat.TRANSLUCENT;
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
/**
* 这边似乎需要做一个版本号的判断
*/
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;
mWindowManager.addView(mViewBubble, mLayoutParams);
mViewBubble.setVisibility(GONE);
}
其中: mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight();,用此来获取seekbar的实际宽高;
原因是为了避免在外部设置控件宽高为match_parent或者wrap_content的时候,MeasureSpec的getSize方法,获取的是父控件的长宽,不合理;
mLayoutParams.gravity= Gravity.LEFT | Gravity.TOP : 将WindowManager的原点设置为 (0,0),便于位置的控制;
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION; 普通应用功能程序窗口
参考:http://blog.csdn.net/listening_music/article/details/7169330
最后在windowManager上add mViewBubble;
- 步骤2:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:{
float x = event.getRawX();
int[] location = new int[2];
this.getLocationInWindow(location);
if (mBubbleAbove) {
mLocalY = location[1] - mHeight - mBubbleHeight + mBubbleOffsetPosition;
}else {
mLocalY = location[1] + mBubbleOffsetPosition;
}
mLocalX = x - mBubbleWidth/2;
showBubble(mLocalX, mLocalY);
mViewBubble.setVisibility(VISIBLE);
break;
}
case MotionEvent.ACTION_MOVE:{
float x = event.getRawX();
int[] location = new int[2];
this.getLocationInWindow(location);
if (mBubbleAbove) {
mLocalY = location[1] - mHeight - mBubbleHeight + mBubbleOffsetPosition;
}else {
mLocalY = location[1] + mBubbleOffsetPosition;
}
/**
* 利用location计算出 控件在屏幕中的坐标
* getMeasuredWidth() 获得measure后的控件的实际宽度
*/
if (location[0] + mThumbOffset * 2 > x){
x = location[0] + mThumbOffset * 2;
}else if (location[0] + mWidth - mThumbOffset * 2 < x){
x = location[0] + mWidth - mThumbOffset * 2;
}
mLocalX = x - mBubbleWidth/2;
showBubble(mLocalX, mLocalY);
break;
}
case MotionEvent.ACTION_UP:{
mViewBubble.setVisibility(GONE);
break;
}
case MotionEvent.ACTION_CANCEL:{
mViewBubble.setVisibility(GONE);
break;
}
default:{
break;
}
}
return super.onTouchEvent(event);
}
核心代码:
float x = event.getRawX();
int[] location = new int[2];
this.getLocationInWindow(location);
if (mBubbleAbove) {
mLocalY = location[1] - mHeight - mBubbleHeight + mBubbleOffsetPosition;
}else {
mLocalY = location[1] + mBubbleOffsetPosition;
}
/**
* 利用location计算出 控件在屏幕中的坐标
* getMeasuredWidth() 获得measure后的控件的实际宽度
*/
if (location[0] + mThumbOffset * 2 > x){
x = location[0] + mThumbOffset * 2;
}else if (location[0] + mWidth - mThumbOffset * 2 < x){
x = location[0] + mWidth - mThumbOffset * 2;
}
mLocalX = x - mBubbleWidth/2;
showBubble(mLocalX, mLocalY);
break;
1.getRawX():获取手指在整个屏幕中的x坐标
int[] location = new int[2];
this.getLocationInWindow(location);
if (mBubbleAbove) {
mLocalY = location[1] - mHeight - mBubbleHeight + mBubbleOffsetPosition;
}else {
mLocalY = location[1] + mBubbleOffsetPosition;
}
getLocationInWindow 获取当前控件在整个屏幕中的位置;坐标指定点为左下角;(相当于,控件从左下角开始绘制)
mBubbleAbove: 设置bubble绘制在seekbar的上方还是下方;
mHeight - mBubbleHeight: mHeight:控件高度,mBubbleHeight:bubble的高度;
mLocalY = location[1] - mHeight - mBubbleHeight + mBubbleOffsetPosition; 作用是将bubble移动到seekbar的上方;
mBubbleOffsetPosition: 该属性可在布局中使用,设置bubble在Y轴上的偏移量;
if (location[0] + mThumbOffset * 2 > x){
x = location[0] + mThumbOffset * 2;
}else if (location[0] + mWidth - mThumbOffset * 2 < x){
x = location[0] + mWidth - mThumbOffset * 2;
}
用于判定bubble不越出范围;
- showBubble(mLocalX, mLocalY);
private void showBubble(float localX, float localY){
mLayoutParams.x = (int) localX;
mLayoutParams.y = (int) localY;
if (null != mTvBubble) {
mTvBubble.setText(String.valueOf(this.getProgress()));
}
mWindowManager.updateViewLayout(mViewBubble, mLayoutParams);
}
更新View;
使用方法:
可直接在布局中直接使用;
提供以下自定义属性:
<attr name="bubble" format="reference"/>
<attr name="bubble_text_view" format="integer"/>
<attr name="bubble_offset_position" format="dimension"/>
<attr name="bubble_is_above" format="boolean"/>
bubble:指定bubble的布局文件;
bubble_text_view:指定 bubble布局下的textView的id;(已弃用,该为通过函数传入)
bubble_offset_position:指定bubble的相对偏移量;
bubble_is_above:设置bubble是在上方还是下方;
提供两个方法:
public void setBubble(ViewGroup bubble){
mViewBubble = (ViewGroup) bubble;
}
public void setBubbleTextView(int bubbleTextViewId){
if (null != mViewBubble) {
mTvBubble = (TextView) mViewBubble.findViewById(bubbleTextViewId);
}
}
setBubble: 设置bubble的布局文件;
setBubbleTextView: 设置bubble布局下的textview控件,用于显示数据;若不指定,则默认为null;
存在问题:
TextView 和bubble的布局文件需要关联在一起;
但是这样做的好处就是,简化了textview在bubble中的属性设置
-
442 Star
-
65 Star
-
0 Star
-
21 Star
-
0 Star
-
2 Star
-
10207 Star
-
597 Star