Android自定义可长按 可点击不可拖动的SeekBar
Posted IyangcLove
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义可长按 可点击不可拖动的SeekBar相关的知识,希望对你有一定的参考价值。
项目中遇见一个奇怪的需求 就是SeekBar不可点击 ,可长按 和拉着拖拽点拖动。一开始想着很简单嘛。屏蔽吊点击事件,SeekBar自带长按事件,差不多就搞定了,然而是我太天真啊,SeekBar自带的长按监听是无效的。我通过网上查寻资料 有一些简单的方法实现不可单击的 有实现长按的,然后我根据这些方式自己写了一个 满足需求的自定义控件。现在依次说下吧。
1. 不可点击的SeekBar
这个可以直接通过seekBar的监听 修改SeekBar的进度 让其达到显示的效果。
可以重写SeekBar 设置其setOnSeekBarChangeListener监听方法
此方式点击SeekBar是触发了 3个方法的 只是进度条没让他跳过去而已
可以看看打印的Log
/**
* 不可点击 长按无效的seekbar
*/
public class MySeekBar extends SeekBar {
private int oldsign;
public MySeekBar(Context context) {
super(context);
init();
}
public MySeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public MySeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
if(progress>oldsign+3||progress<oldsign-3){
seekBar.setProgress(oldsign);
return;
}
seekBar.setProgress(progress);
oldsign = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
seekBar.setProgress(oldsign);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
也可以直接在Activity中用普通的SeekBar进行设置
//sb为原生的SeekBar
oldsign = sb.getProgress();
sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// Toast.makeText(MainActivity.this, "onProgressChanged"+progress, 0).show();
Log.e("Chuck", "onProgressChanged");
if(progress>oldsign+3||progress<oldsign-3){
seekBar.setProgress(oldsign);
return;
}
seekBar.setProgress(progress);
oldsign = progress;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Log.e("Chuck", "onStartTrackingTouch");
seekBar.setProgress(oldsign);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Log.e("Chuck", "onStopTrackingTouch");
}
});
这样都是可以达到 不可点击 可拖拽的效果的,接下来看看 可长按的SeekBar。
长按监听的话 是自己通过onTouch事件监听判断的然后写了一个接口把长按事件传递出来,详细代码如下:
public class SeekBarLongClick extends SeekBar implements OnTouchListener {
private onLong longClick;
private int oldsign;//获取上次更改后的点击状态
/**
* 长按接口
*/
public interface onLong {
public boolean onLongClick(SeekBarLongClick seekBar);
}
private onChange SeekBarChange;
/**
* 进度改变接口
*/
public interface onChange {
public void onStopTrackingTouch(SeekBarLongClick seekBar);
public void onStartTrackingTouch(SeekBarLongClick seekBar);
public void onProgressChanged(SeekBarLongClick seekBar, int progress,
boolean fromUser);
}
private Handler hand;
private Runnable runable;
private Thread th;
public static int i = 0;
private boolean isStop = false;
public static int pp = 0;
public int index = 0;
public SeekBarLongClick(Context context) {
this(context, null);
// TODO Auto-generated constructor stub
}
public SeekBarLongClick(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnTouchListener(this);
this.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
if (SeekBarChange != null) {
SeekBarChange.onStopTrackingTouch(SeekBarLongClick.this);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
seekBar.setProgress(oldsign);
if (SeekBarChange != null) {
SeekBarChange.onStartTrackingTouch(SeekBarLongClick.this);
}
}
@Override
public void onProgressChanged(final SeekBar seekBar,
final int progress, boolean fromUser) {
if(progress>oldsign+3||progress<oldsign-3){
seekBar.setProgress(oldsign);
}else{
seekBar.setProgress(progress);
oldsign = progress;
}
if (SeekBarChange != null) {
SeekBarChange.onProgressChanged(SeekBarLongClick.this, oldsign,
fromUser);
}
hand = getHandler(1, SeekBarLongClick.this, oldsign);
}
});
/**
* 为runable 赋值
*/
runable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
do {
i++;
try {
Thread.sleep(400);
Message msg = hand.obtainMessage();
msg.arg1 = i;
msg.sendToTarget();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} while (isStop);
}
};
}
/**
* 获取一个handler 对象
* @param j 0代表onTouch 1代表onChange
* @param v 视图对象
* @param progress 进度
* @return 返回一个handler对象
*/
public Handler getHandler(final int j, final View v, final int progress) {
Handler h = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (j) {
case 0:
if (msg.arg1 == 3) {
if (longClick != null) {
longClick.onLongClick(SeekBarLongClick.this);
isStop = false;
}
}
break;
case 1:
if (msg.arg1 == 1) {
pp = progress;
}
if (msg.arg1 == 2) {
if (pp != progress) {
i = 0;
}
}
if (msg.arg1 == 3) {
i = 0;
if (pp == progress) {
if (longClick != null) {
longClick.onLongClick(SeekBarLongClick.this);
isStop = false;
}
}
}
break;
}
super.handleMessage(msg);
}
};
return h;
}
/**
* 设置长按事件
* @param longClick
*/
public void setOnLongSeekBarClick(onLong longClick) {
this.longClick = longClick;
}
/**
* 设置进度改变事件
* @param change
*/
public void setOnSeekBarChange(onChange change) {
this.SeekBarChange = change;
}
@Override
public boolean onTouch(final View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float x2 = event.getX();
float y2 = event.getY();
isStop = true;
th = new Thread(runable);
th.start();
i = 0;
hand = getHandler(0, v, 0);
break;
case MotionEvent.ACTION_UP:
isStop = false;
break;
}
return false;
}
}
接下来就是可长按 可拖动 不可点击,而且点击不会触发SeekBar监听的自定义SeekBar了。控件继承FrameLayout,在SeekBar上面盖上了一层透明的ImageView,通过ImageView的onTouch事件 相应的对SeekBar进行操作,控件中还添加了一个添加关键点的功能,主要用于 用于点击关键点时才能跳到对应的进度,不添加关键点 看起来 用起来就和普通的seekbar一模一样。拖动进度 是根据SeekBar控件的宽度来的,所以要计算一些坐标 和控件宽高,使用时在Activity的onWindowFocusChanged()方法中设置。如果是固定宽高 可直接写死。详细代码如下:
public class SeekBarView extends FrameLayout {
private onLong longClick; // 长按监听
private MotionEvent motionEvent;// 移动监听的对象
private float scale; // 当前比例
private Context context;
boolean isMove = false; // 当前是否是移动动作
float xDown, yDown, xUp;
private SeekBar seekBar;
private ImageView image;
private RelativeLayout rl_container;
private int marginLeft;
private int seekBarWidth;
private int moveWidth;
private boolean longClicked;
/**
* 长按接口
*
* @author terry
*
*/
public interface onLong {
public boolean onLongClick(SeekBarView seekBar);
}
private onChange SeekBarChange;// SeekBar进度监听
/**
* 进度改变接口,仿SeekBar
*
* @author terry
*
*/
public interface onChange {
public void onStopTrackingTouch(SeekBarView seekBar);
public void onStartTrackingTouch(SeekBarView seekBar);
/**
* @param seekBar
* SeekBarView对象
* @param progress
* 进度条
* @param fromUser
* 只有拖动 为true,长按 不触发此方法,点击关键点为false.
*/
public void onProgressChanged(SeekBarView seekBar, int progress,
boolean fromUser);
}
public SeekBarView(Context context) {
super(context);
}
public SeekBarView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
View view = LayoutInflater.from(context).inflate(R.layout.view_seekbar,
null);
// initView(view);
addView(view);
}
public SeekBarView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs);
}
public void initView(View view) {
seekBarWidth = getWidth();
int [] ints=new int[2];
getLocationOnScreen(ints);
marginLeft = ints[0];
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay()
.getMetrics(displayMetrics);
moveWidth = seekBarWidth - dip2px(context, 20);
// 播放时间为1000秒,
seekBar = (SeekBar) view.findViewById(R.id.seekbar);
scale = (float) seekBar.getMax() / (float) moveWidth;
rl_container = (RelativeLayout) view.findViewById(R.id.rl_container);
image = (ImageView) view.findViewById(R.id.image);
//motionEvent获取到的是Activity 的 坐标······ 与Seekbar的坐标 x 有margin距离
image.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int x = (int) motionEvent.getX();
if (Math.abs(x - xDown - marginLeft) < 10) {
seekBar.setProgress((int) ((int) (x - marginLeft) * scale));
isMove = false;
if (longClick != null) {
longClick.onLongClick(SeekBarView.this);
longClicked=true;
}
}
return false;
}
});
image.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (motionEvent == null) {
motionEvent = event;
}
// 当按下时处理
if (event.getAction() == MotionEvent.ACTION_DOWN) {
xDown = event.getX();
yDown = event.getY();
}// 松开处理
if (event.getAction() == MotionEvent.ACTION_UP) {
isMove = false;
xUp = event.getX();
if (SeekBarChange != null&&!longClicked) {
SeekBarChange.onStopTrackingTouch(SeekBarView.this);
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
float x = event.getX();
final int progress = seekBar.getProgress();
// 如果点击位置是当前进度位置对应坐标的20px以内就认为点击到了当前位置 ,拖动可调整进度条
if (!isMove && Math.abs(progress - scale * xDown) <= 20) {
isMove = true;
}
if (isMove) {
seekBar.setProgress((int) (scale * x));
if (SeekBarChange != null) {
SeekBarChange.onProgressChanged(SeekBarView.this,
(int) (scale * x), true);
}
} else {
// 其他模式
}
}
return false;
}
});
}
public void setMax(int max) {
seekBar.setMax(max);
scale= (float) seekBar.getMax() / (float) moveWidth ;
}
public int getMax() {
return seekBar.getMax();
}
public void setProgress(int progress) {
seekBar.setProgress(progress);
}
public int getProgress() {
return seekBar.getProgress();
}
public void addKeyPoint(final int[] pointIndexs) {
for (int i = 0; i < pointIndexs.length; i++) {
final int tempI = i;
ImageView imageView = new ImageView(context);
int imgSize = dip2px(context, 20);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(imgSize, imgSize);
// 距离左边的位置···因为图标本身长度为20dp 所以这里需要减去10dp
int marginLeft = (int) (pointIndexs[i] / scale);
layoutParams.leftMargin = marginLeft;
imageView.setLayoutParams(layoutParams);
imageView.setImageResource(R.drawable.main_homework_up);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
rl_container.addView(imageView);
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
seekBar.setProgress(pointIndexs[tempI]);
if (SeekBarChange != null) {
SeekBarChange.onProgressChanged(SeekBarView.this,
pointIndexs[tempI], false);
}
}
});
}
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
private int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 设置长按事件
*
* @param longClick
*/
public void setOnLongSeekBarClick(onLong longClick) {
this.longClick = longClick;
}
/**
* 设置进度改变事件
*
* @param change
*/
public void setOnSeekBarChange(onChange change) {
this.SeekBarChange = change;
}
}
//对应的xml布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<SeekBar
android:progressDrawable="@drawable/seekbar_img"
android:thumb="@drawable/public_play_volume_dragpoint"
android:id="@+id/seekbar"
android:background="#00ff0000"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="8dp"
android:minHeight="8dp"
android:layout_gravity="center"
/>
<ImageView
android:id="@+id/image"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#0000ff00"
/>
<RelativeLayout
android:background="#000000ff"
android:id="@+id/rl_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_centerInParent="true"
android:gravity="center_vertical"/>
</FrameLayout>
//在Activity中调用
/**
* @param hasFocus
*
* 第一次界面渲染完成时 对自定义的SeekBarView进行初始化 和 测量。
*/
boolean isOne=false;
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if(isOne){
isOne=false;
barView.initView(barView);
barView.setMax(1000);
//添加关键点。不添加即无效果
barView.addKeyPoint(new int[]{100,300,620,530,1000,700});
barView.setOnLongSeekBarClick(new SeekBarView.onLong() {
@Override
public boolean onLongClick(SeekBarView seekBar) {
Log.e("Chuck", "onLongClick() 方法触发 progress = " +seekBar. getProgress());
return false;
}
});
barView.setOnSeekBarChange(new SeekBarView.onChange() {
//手指离开屏幕,可能长按,可能拖动,判断按下屏幕之前 计时器的状态 进行相应的操作
@Override
public void onStopTrackingTouch(SeekBarView seekBar) {
Log.e("Chuck", "onStopTrackingTouch()方法触发 progress = " + seekBar.getProgress());
if(!playerTimer.isStarted()){
playerTimer.start();
}
}
@Override
public void onStartTrackingTouch(SeekBarView seekBar) {
}
//fromUser 只有拖动的时候为true,长按不触发此方法,点击关键点时为false.
@Override
public void onProgressChanged(SeekBarView seekBar, int progress,
boolean fromUser) {
if(fromUser){
if(playerTimer.isStarted()){
playerTimer.stop();
}
tv.setText(progress+"----------------"+barView.getMax());
}
Log.e("Chuck", "onProgressChanged()方法触发 progress=" + progress + " "+"fromUser="+fromUser );
}
});
playerTimer.start();
}
super.onWindowFocusChanged(hasFocus);
}
最终的效果
Demo下载地址(AndroidStudio写的比较大额)
GitHub地址
CSDN地址
以上是关于Android自定义可长按 可点击不可拖动的SeekBar的主要内容,如果未能解决你的问题,请参考以下文章