这效果实现好久了。这里再复述一遍,也是帮自己再记忆一次。
先上图
以上 是单击和长按模式下的效果图
先来看下整体的UI图
这里首先要隐藏下半部分,之所以写两块圆形图是为了点击事件和onTouch区分开
接下来第一步,效果图中看到的点击说话 和倒计时的文字实现
这里是自定义的一个TimeTextView 继承自TextView ,主要作用就是实现这个计时效果。
以下是代码部分,本人主要是用handler 实现的计时效果
public class TimeTextView extends TextView {
private int sTime;
private TimeTextView timeTextView;
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
sTime++;
timeTextView.setText(String.valueOf(sTime)+"s: 60s");
handler.sendEmptyMessageDelayed(1,1000);
}
};
public TimeTextView(Context context) {
super(context);
initView();
}
public TimeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public TimeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
//初始化,即一开始显示的点击说话
private void initView() {
timeTextView= this;
timeTextView.setText(getContext().getResources().getString(R.string.click_say));
}
public void downTime(){
handler.sendEmptyMessage(1);
}
//停止计时后,记得把handler remove掉
public void stopTime(){
sTime=0;
handler.removeCallbacksAndMessages(null);
}
//取消之后恢复文字说明
public void cancleTime(){
timeTextView.setText(getContext().getResources().getString(R.string.click_say));
}
public int getsTime(){
return sTime;
}
}
接下来是圆形的进度条效果。
这里本人利用 CircularProgressDrawable +ObjectAnimator属性动画实现的 圆形进度条效果,
首先设置原型进度条的样式
private void initRecordingChang() {
drawableChang = new CircularProgressDrawable.Builder()
.setRingWidth(getResources().getDimensionPixelSize(R.dimen.dp_2))
.setOutlineColor(getResources().getColor(R.color.light_purple))
.setRingColor(getResources().getColor(R.color.light_purple))
.setCenterColor(getResources().getColor(R.color.light_purple))
.create();
recordingChang.setImageDrawable(drawableChang);
}
然后是设置动画效果以及属性动画的一些属性
private ObjectAnimator prepareStyle2Animation(CircularProgressDrawable drawable) {
ObjectAnimator progressAnimation = ObjectAnimator.ofFloat(drawable, CircularProgressDrawable.PROGRESS_PROPERTY,
0f, 1f);
// progressAnimation.setRepeatCount(ObjectAnimator.RESTART);
//设置动画效果的模式。
progressAnimation.setRepeatMode(ObjectAnimator.RESTART);
//设置动画效果的持续事件
progressAnimation.setDuration(60000);
//这个是插值器。详细的可以自行百度;
progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
//动画监听
progressAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
if (tvOnlyTime.getsTime()==6){
tvOnlyTime.stopTime();
tvOnlyTime.cancleTime();
length = stopRecoding();
initAnimation();
sendRecording(); //发送录音
}
if (tvTouchTime.getsTime()==6){
tvTouchTime.stopTime();
tvTouchTime.cancleTime();
currentAnimationChang.cancel();
}
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
return progressAnimation;
}
OK。把主要的两个部分已经贴出,最后只要实现单击和长按的逻辑 即可
首先是单击事件,
这里先梳理下有几种状态,
1、初始状态转向录音状态
2、录音状态转向停止(暂停)
3、发送录音
4、取消发送(由效果图可看出这个是独立的一个取消按钮,与中间的圆形进度条点击无关。但是这关乎回到初始化。所以这里写一些,但是不在圆形按钮的点击事件内)
点击的模块
接下来上代码
findViewById(R.id.recording_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (recording_type == SOCKET_RECORDING_TYPE_INIT) { // 录音由状态转为录音状态
if (currentAnimation != null) {
currentAnimation.cancel();
}
recording_type = SOCKET_RECORDING_TYPE_RECONDING;
length=0;
currentAnimation.start();
changeRecodingView(SOCKET_RECORDING_TYPE_INIT);
startRecording(); // 开启录音
tvOnlyTime.downTime();
}else if (recording_type == SOCKET_RECORDING_TYPE_RECONDING){ ///暂停录音 等待播主决定发送还是取消
currentAnimation.cancel(); // 动画停止
changeRecodingView(SOCKET_RECORDING_TYPE_RECONDING);
recording_type = SOCKET_RECORDING_TYPE_SEND;
length = stopRecoding();
tvOnlyTime.stopTime();
}else if (recording_type == SOCKET_RECORDING_TYPE_SEND){ //用户按下发送 录音 一切状态归init
changeRecodingView(SOCKET_RECORDING_TYPE_SEND);
recording_type = SOCKET_RECORDING_TYPE_INIT;
initAnimation();
sendRecording(); //发送录音
tvOnlyTime.cancleTime();
}
}
});
//上图中、中间控件的切换,其实就是开始暂停
private void changeRecodingView(int recordingTypeInit) {
switch (recordingTypeInit) {
case SOCKET_RECORDING_TYPE_INIT:
recordingSend.setVisibility(View.GONE);
recordingPause.setVisibility(View.VISIBLE);
break;
case SOCKET_RECORDING_TYPE_RECONDING:
recordingSend.setVisibility(View.VISIBLE);
recordingPause.setVisibility(View.GONE);
break;
case SOCKET_RECORDING_TYPE_SEND:
recordingSend.setVisibility(View.GONE);
recordingPause.setVisibility(View.GONE);
// count_second.setText("点击说话");
break;
}
}
接下来就是长按模块了
这块主要处理onTouch事件,再down的时候记录初始坐标,再move的时候计算滑动位置是否超出指定范围,给出相应的提示信息,
findViewById(R.id.recording_container2).setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
eventStartY=Math.abs(event.getY());
length=0;
try {
if (currentAnimationChang != null) {
currentAnimationChang.cancel();
}
startRecording();
tvTouchTime.downTime();
currentAnimationChang.start();
} catch (Exception e) {
}
mTvTouchExplain.setVisibility(VISIBLE);
mTvTouchExplain.setText("手指滑出此区域,取消发送");
mTvTouchExplain.setBackgroundColor(getResources().getColor(R.color.pur_708fcc));
return true;
case MotionEvent.ACTION_MOVE:
//宽度不大准 但允许误差
if (event.getY()<0||event.getY()>cusRecordVoiceView.getHeight()||event.getX()<20||event.getX()>cusRecordVoiceView.getWidth()){
mTvTouchExplain.setText("松开手指取消发送");
mTvTouchExplain.setBackgroundColor(getResources().getColor(R.color.red_F40808));
}else {
mTvTouchExplain.setText("手指滑出此区域,取消发送");
mTvTouchExplain.setBackgroundColor(getResources().getColor(R.color.pur_708fcc));
}
return true;
case MotionEvent.ACTION_UP:
mTvTouchExplain.setVisibility(GONE);
eventEndY=Math.abs(event.getY());
currentAnimationChang.cancel(); // 动画停止并回归初始状态
initAnimationChang();
length = stopRecoding();
tvTouchTime.stopTime();
if (event.getY()<0||event.getY()>cusRecordVoiceView.getHeight()||event.getX()<20||event.getX()>cusRecordVoiceView.getWidth()){
}else {
try {
//发送录音
sendRecording();
tvTouchTime.cancleTime();
} catch (Exception e) {
e.printStackTrace();
tvTouchTime.cancleTime();
Toast.makeText(context, R.string.send_failure_please, Toast.LENGTH_SHORT).show();
}
}
return true;
default:
discardRecording();
return false;
}
}
});
OK 至此。整体效果就实现了。