IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 实现一个可以手势调整亮度,声音和进度条的控件 -> 正文阅读

[移动开发]实现一个可以手势调整亮度,声音和进度条的控件

项目开发中当遇到需要我们自己手动实现屏幕滑动,或者某个控件滑动来控制屏幕亮度,声音及进度条等功能的时候我们会找网上的轮子,但是很多时候比较坑,实现起来要尝试多次也不一定能够找到理想的,下面展示一个成熟项目中的手动滑动实现方式,一般知乎,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传递进去便可。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-04 17:39:38  更:2021-09-04 17:40:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:13:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码