项目开发中当遇到需要我们自己手动实现屏幕滑动,或者某个控件滑动来控制屏幕亮度,声音及进度条等功能的时候我们会找网上的轮子,但是很多时候比较坑,实现起来要尝试多次也不一定能够找到理想的,下面展示一个成熟项目中的手动滑动实现方式,一般知乎,B站,腾讯视频,爱奇艺等都是使用该功能。
最重要的是丝滑,流畅。
下面开始实现步骤一:
自定义一个手势UI类,一般播放器等都是在Controller中实现,这里写个MyGestureView类如下,由于注释写的比较详细,这里不做讲解如下:
/**
* author : AndyYuan
* e-mail : 01810788@smg.cn
* date : 2021/9/3 000315:06
* desc : 手动实现的控制器
* version: 2.0
*/
public class MyGestureView extends FrameLayout implements Handler.Callback {
//最外层容器
private LinearLayout container;
//显示进度百分比
private TextView tvPercent;
//百分比进度条
private ProgressBar pbPercent;
private Handler handler;
//手势监听音量
private GestureDetector gestureDetector;
private AudioManager audioManager;
private WindowManager windowManager;
//当前声音
private int nowVolume = -1;
//当前的屏幕亮度
private float mBrightness;
protected boolean isFullScreen = false;
private int currentDuration = 0;
private int mSeekPosition;
//控制屏幕手势
private boolean mFirstTouch;
private boolean mChangePosition;
private boolean mChangeBrightness;
private boolean mChangeVolume;
//当前界面
private Activity context;
//seekBar最大进度
private final int SEEK_MAX = 100;
//控制条自动隐藏时间
private final int DEFAULT_TIME = 5 * 1000;
//隐藏控制条
private final int FADE_OUT = 1;
//隐藏快进框
private final int DIALOG_TIME = 3 * 1000;
private final int FADE_DIALOG = 2;
public MyGestureView(Context context) {
super(context);
initView(context);
}
public MyGestureView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public MyGestureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.gesture_layout, this);
container = findViewById(R.id.center_container);
tvPercent = findViewById(R.id.tv_percent);
pbPercent = findViewById(R.id.pro_percent);
handler = new Handler(this);
pbPercent.setMax(SEEK_MAX);
}
public void setActivity(Activity activity) {
this.context = (Activity) activity;
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
gestureDetector = new GestureDetector(activity, new VideoPlayGestureListener());
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case FADE_OUT:
case FADE_DIALOG:
container.setVisibility(View.INVISIBLE);
break;
default:
break;
}
return false;
}
/**
* 直播、点播横屏播放的时候监听手势变化 控制音量、亮度和(点播)播放的快进、快退
*/
class VideoPlayGestureListener extends GestureDetector.SimpleOnGestureListener {
private float fx;
private float fy;
private float halfScreenWidth;
@Override
public boolean onDown(MotionEvent e) {
fx = e.getX();
fy = e.getY();
//当横屏的时候根据屏幕高度来判断
halfScreenWidth = ScreenUtil.width(context).px / 2;
nowVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
mBrightness = context.getWindow().getAttributes().screenBrightness;
//手势控制
mFirstTouch = true;
mChangePosition = false;
mChangeBrightness = false;
mChangeVolume = false;
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float deltaX = e1.getX() - e2.getX();
float deltaY = e1.getY() - e2.getY();
container.setVisibility(View.VISIBLE);
//调节音量大小,在屏幕的右侧,这里对按下去的地方进行判断,之前的e2.getX();
Log.e("myview", "fx: = " + fx + ", deltaX= " + deltaX + ", deltaY= " + deltaY);
if (mFirstTouch) {
mChangePosition = Math.abs(distanceX) >= Math.abs(distanceY);
if (!mChangePosition) {
//半屏宽度
int halfScreen = ScreenUtil.width(context).px / 2;
if (e2.getX() > halfScreen) {
mChangeVolume = true;
} else {
mChangeBrightness = true;
}
}
if (mChangePosition) {
mChangePosition = true;
}
mFirstTouch = false;
}
if (mChangePosition) {
slideToChangePosition(deltaX);
} else if (mChangeBrightness) {
slideToChangeBrightness(deltaY);
} else if (mChangeVolume) {
slideToChangeVolume(deltaY);
}
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return super.onFling(e1, e2, velocityX, velocityY);
}
}
/**
* 滑动改变声音大小
*
* @param deltaY
*/
protected void slideToChangeVolume(float deltaY) {
int streamMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int height = getMeasuredHeight();
float deltaV = deltaY * 2 / height * streamMaxVolume;
float index = nowVolume + deltaV;
if (index > streamMaxVolume) index = streamMaxVolume;
if (index < 0) index = 0;
int percent = (int) (index / streamMaxVolume * 100);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) index, 0);
//设置进度条显示
pbPercent.setProgress(percent);
tvPercent.setText(percent + "%");
}
/**
* 调整亮度大小
*
* @param deltaY
*/
protected void slideToChangeBrightness(float deltaY) {
Window window = context.getWindow();
WindowManager.LayoutParams attributes = window.getAttributes();
int height = getMeasuredHeight();
if (mBrightness == -1.0f) mBrightness = 0.5f;
float brightness = deltaY / height * 1.0f + mBrightness;
if (brightness < 0) {
brightness = 0f;
}
if (brightness > 1.0f) brightness = 1.0f;
int percent = (int) (brightness * 100);
attributes.screenBrightness = brightness;
window.setAttributes(attributes);
pbPercent.setProgress(percent);
tvPercent.setText(percent + "%");
}
/**
* 快进调整
*
* @param deltaX
*/
protected void slideToChangePosition(float deltaX) {
deltaX = -deltaX;
int width = getMeasuredWidth();
int duration = (int) 645241;
int currentPosition = (int) 123456;
int position = (int) (deltaX / width * 120000 + currentPosition);
if (position > duration) position = duration;
if (position < 0) position = 0;
mSeekPosition = position;
pbPercent.setProgress(position / duration * 100);
tvPercent.setText(String.format("%s/%s", stringForTime(position), stringForTime(duration)));
Log.e("slidePosition:", " duration: " + duration + ", currentPosition: " + currentPosition + ", seekPosition: " + mSeekPosition);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (gestureDetector != null) {
gestureDetector.onTouchEvent(event);
}
return true;
}
/**
* 手势添加拦截事件
*
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//手势添加拦截事件
gestureDetector.onTouchEvent(ev);
//处理手势结束
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_UP:
//endGesture();
Log.e("slideposition:", "dispatchTouchEvent!!!");
if (mSeekPosition > 0) {
//videoView.seekTo(mSeekPosition);
mSeekPosition = 0;
}
handler.sendEmptyMessageDelayed(FADE_DIALOG, DIALOG_TIME);
break;
case MotionEvent.ACTION_CANCEL:
mSeekPosition = 0;
break;
}
return super.dispatchTouchEvent(ev);
}
/**
* 格式化时间
*/
public static String stringForTime(int timeMs) {
int totalSeconds = timeMs / 1000;
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
if (hours > 0) {
return String.format(Locale.getDefault(), "%d:%02d:%02d", hours, minutes, seconds);
} else {
return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds);
}
}
}
这个类中由于是控制器和Activity是分离操作的,所以让Activity和控制器之前链接桥梁是setActivity方法。
步骤二简单的布局文件即控制器中的资源文件R.layout.gesture_layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<!--声音显示器-->
<LinearLayout
android:id="@+id/center_container"
android:layout_width="160dp"
android:layout_height="120dp"
android:layout_centerInParent="true"
android:background="#96000000"
android:gravity="center"
android:orientation="vertical"
android:visibility="visible">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:src="@drawable/dkplayer_ic_action_volume_up" />
<TextView
android:id="@+id/tv_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="100"
android:textColor="@android:color/white"
android:textSize="14sp" />
<ProgressBar
android:id="@+id/pro_percent"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="100dp"
android:layout_height="3dp"
android:max="100"
android:progressDrawable="@drawable/dkplayer_layer_progress_bar" />
</LinearLayout>
</RelativeLayout>
其中有个ScreenUtil是测量当前屏幕的工具类,使用不使用没有多少关系,去掉也行。
在引用的时候直接:
<com.XXX你的包名.MyGestureView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:background="@color/text_gray"
android:text="可以滑动" />
在调用的地方别忘记把当前Activity传递进去便可。
|