(原创)视频播放器的手势控制工具类
最近在做一个视频播放器
现在市场上,一个比较完善的视频播放器
大概具有以下功能:
快进、快退、声音、亮度控制
这一次就根据这几个基础的功能
通过系统的手势控制类GestureDetector来完成
做了一个实用的工具类
只需要简单的配置
就可以实现对视频播放器控件的手势进行监听
工具类内部实现了相关功能,不需要视频播放器自己写
实现了代码解耦,也方便复用
下面就开始介绍这个工具类的使用,以及实现的原理。
先把工具类的实际代码贴上来
public class VideoGestureControlUtil {private static final String TAG = "VideoGestureControlUtil";private Context mContext;private GestureDetector mGestureDetector;//手势类private MyGestureListener myGestureListener;//手势监听private OnVideoControlListener videoControlListener;//视频View回调监听private Rect VideoViewRect = null;//视频控件范围//状态相关private static int SCROLL_FLAG = 0;//记录状态private static final int SCROLL_FLAG_RESET = 0;//初始状态,无任何操作private static final int SCROLL_FLAG_TOPBOTTOM_LEFT = 1;//左边屏幕上下滑动private static final int SCROLL_FLAG_TOPBOTTOM_RIGHT = 2;//右边屏幕上下滑动private static final int SCROLL_FLAG_LEFTRIGHT = 3;//左右滑动//拖动相关protected static final float FLIP_DISTANCE = 50;//确定滑动方向的最小滑动距离private int SCROLL_VIDEO_SCROLL_RANGE = 1000;//拖动范围 0~1000private float SCROLL_VIDEO_PLAY_RANGE = 0.25f;//视频可拖动部分的范围//视频进度相关private float SCROLL_VIDEO_PLAY_INDEX = 0f;//拖动的视频进度private double videoIndex = 0;//拖动的视频毫秒数private double newVideoIndex;//拖动结束后视频的位置,单位毫秒private double SCROLL_VIDEO_PLAYING_INDEX = 0;//视频播放位置private double SCROLL_VIDEO_PLAYING_INDEX_CATCH = 0;//缓存的视频播放位置(手指按下去的视频播放位置)private double SCROLL_VIDEO_LENTH = 0;//视频总长度 单位毫秒//声音和亮度相关private AudioManager systemService;private int SCROLL_VIDEO_VOICE_INDEX = 0;//拖动的视频声音private double SCROLL_VIDEO_LIGHT_INDEX = 0;//拖动的视频亮度private int SCROLL_VOICE_MAX = 0;//声音总长度private int SCROLL_LIGHT_MAX = 255;//亮度总长度private int SCREEN_LIGHT = 0;//屏幕亮度private int SCREEN_LIGHT_CATCH = 0;//缓存的屏幕亮度private int SCREEN_VOICE = 0;//音量大小//弹框部分private Dialog indexDialog;//弹框private View videoDialogView;//弹框Viewprivate ImageView indeximg;private LinearLayout indexll, voicell;private TextView indextv;private ImageView voiceLightImg;private SeekBar voiceLightsb;private WindowManager.LayoutParams indexDialogLp;private DisplayMetrics displayMetrics;public VideoGestureControlUtil(Context context, View view) {myGestureListener = new MyGestureListener();this.mContext = context;systemService = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);SCROLL_VOICE_MAX = systemService.getStreamMaxVolume(AudioManager.STREAM_MUSIC);mGestureDetector = new GestureDetector(context, myGestureListener);mGestureDetector.setOnDoubleTapListener(myGestureListener);LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);videoDialogView = layoutInflater.inflate(R.layout.video_gesture_dialog, null);indexll = videoDialogView.findViewById(R.id.indexll);voicell = videoDialogView.findViewById(R.id.voicell);indeximg = videoDialogView.findViewById(R.id.indeximg);indextv = videoDialogView.findViewById(R.id.indextv);voiceLightImg = videoDialogView.findViewById(R.id.voice_light_img);voiceLightsb = videoDialogView.findViewById(R.id.voice_light_sb);indexDialog = new Dialog(context);/*随意定义个Dialog*/Window dialogWindow = indexDialog.getWindow();/*实例化Window*/indexDialogLp = dialogWindow.getAttributes();/*实例化Window操作者*/indexDialogLp.x = 0; // 新位置X坐标indexDialogLp.y = 0; // 新位置Y坐标dialogWindow.setGravity(Gravity.CENTER);dialogWindow.getDecorView().setBackground(null);dialogWindow.setAttributes(indexDialogLp);/*放置属性*/indexDialog.setContentView(videoDialogView, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT));dialogWindow.setDimAmount(0);dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);try {int dividerID = indexDialog.getContext().getResources().getIdentifier("android:id/titleDivider", null, null);View divider = indexDialog.findViewById(dividerID);if (divider != null) {divider.setBackgroundColor(Color.TRANSPARENT);}} catch (Exception e) {//上面的代码,是用来去除Holo主题的蓝色线条e.printStackTrace();}indexDialog.setCanceledOnTouchOutside(false);}public boolean touch(MotionEvent event) {boolean detectedUp = event.getAction() == MotionEvent.ACTION_UP;if (!mGestureDetector.onTouchEvent(event) && detectedUp) {//手指抬起时触发if (SCROLL_FLAG == SCROLL_FLAG_LEFTRIGHT) {//设置当前播放进度videoControlListener.setScrollVideoPlayingIndex(SCROLL_VIDEO_PLAYING_INDEX);}SCROLL_FLAG = SCROLL_FLAG_RESET;VideoViewRect = null;dismissIndexDialog();} else if (event.getAction() == MotionEvent.ACTION_CANCEL) {//熄屏时会触发SCROLL_FLAG = SCROLL_FLAG_RESET;VideoViewRect = null;dismissIndexDialog();}return true;}/** 手势监听类*/class MyGestureListener extends GestureDetector.SimpleOnGestureListener {public MyGestureListener() {super();}@Overridepublic boolean onDoubleTap(MotionEvent e) {Log.e(TAG, "双击");videoControlListener.onDoubleTap();return true;}@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {Log.e(TAG, "单击");videoControlListener.onSingleTap();return true;}@Overridepublic boolean onDoubleTapEvent(MotionEvent e) {Log.e(TAG, "onDoubleTapEvent");return true;}@Overridepublic boolean onContextClick(MotionEvent e) {Log.e(TAG, "onContextClick");return true;}@Overridepublic boolean onDown(MotionEvent e) {Log.e(TAG, "onDown");return true;}@Overridepublic void onShowPress(MotionEvent e) {Log.e(TAG, "onShowPress");}@Overridepublic boolean onSingleTapUp(MotionEvent e) {Log.e(TAG, "onSingleTapUp");return true;}@Overridepublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {switch (SCROLL_FLAG) {case SCROLL_FLAG_RESET:WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);displayMetrics = new DisplayMetrics();windowManager.getDefaultDisplay().getRealMetrics(displayMetrics);if (e1.getX() < FLIP_DISTANCE || e1.getX() > displayMetrics.widthPixels - FLIP_DISTANCE || e1.getY() > displayMetrics.heightPixels - FLIP_DISTANCE) {Log.d(TAG, "onScroll: 无效动作" + e1.getX());//这里主要是处理屏幕边缘滑动的问题,防止和边缘的滑动返回等操作冲突,所以屏蔽了边缘的部分滑动。return true;}if (VideoViewRect == null) {VideoViewRect = videoControlListener.getVideoViewRect();Log.d(TAG, "高度: " + displayMetrics.heightPixels);Log.d(TAG, "宽度: " + displayMetrics.widthPixels);if (VideoViewRect != null) {Log.d(TAG, "left: " + VideoViewRect.left);Log.d(TAG, "right: " + VideoViewRect.right);Log.d(TAG, "top: " + VideoViewRect.top);Log.d(TAG, "bottom: " + VideoViewRect.bottom);indexDialogLp.x = ((VideoViewRect.right - VideoViewRect.left) / 2) - (displayMetrics.widthPixels / 2);indexDialogLp.y = ((VideoViewRect.bottom - VideoViewRect.top) / 2) - (displayMetrics.heightPixels / 2) + VideoViewRect.top;}}//初始化,没有滑动方向if (e1.getX() - e2.getX() > FLIP_DISTANCE || e2.getX() - e1.getX() > FLIP_DISTANCE) {Log.i(TAG, "向左右滑...");SCROLL_FLAG = SCROLL_FLAG_LEFTRIGHT;SCROLL_VIDEO_PLAY_INDEX = 0f;videoIndex = 0;//得到视频长度和播放位置SCROLL_VIDEO_LENTH = videoControlListener.getScrollVideoLenth();SCROLL_VIDEO_PLAYING_INDEX_CATCH = SCROLL_VIDEO_PLAYING_INDEX = videoControlListener.getScrollVideoPlayingIndex();indexll.setVisibility(View.VISIBLE);voicell.setVisibility(View.GONE);showIndexDialog();return true;} else if (e1.getY() - e2.getY() > FLIP_DISTANCE || e2.getY() - e1.getY() > FLIP_DISTANCE) {if (e1.getX() < (displayMetrics.widthPixels / 2)) {//左边上下滑动滑动SCROLL_FLAG = SCROLL_FLAG_TOPBOTTOM_LEFT;Log.i(TAG, "左屏幕向上下滑...");SCROLL_VIDEO_LIGHT_INDEX = 0;voiceLightImg.setImageResource(R.drawable.video_lightimg);try {//当前屏幕亮度SCREEN_LIGHT_CATCH = SCREEN_LIGHT = Settings.System.getInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);voiceLightsb.setMax(SCROLL_LIGHT_MAX);voiceLightsb.setProgress(SCREEN_LIGHT);} catch (Settings.SettingNotFoundException e) {e.printStackTrace();}} else {//右边上下滑动SCROLL_FLAG = SCROLL_FLAG_TOPBOTTOM_RIGHT;Log.i(TAG, "右屏幕向上下滑...");SCROLL_VIDEO_VOICE_INDEX = 0;//当前声音大小SCREEN_VOICE = systemService.getStreamVolume(AudioManager.STREAM_MUSIC);voiceLightImg.setImageResource(R.drawable.video_voiceimg);voiceLightsb.setMax(SCROLL_VOICE_MAX);voiceLightsb.setProgress(SCREEN_VOICE);}indexll.setVisibility(View.GONE);voicell.setVisibility(View.VISIBLE);showIndexDialog();return true;}break;case SCROLL_FLAG_TOPBOTTOM_LEFT://设置亮度if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (!Settings.System.canWrite(mContext)) {Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);intent.setData(Uri.parse("package:" + mContext.getPackageName()));intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);mContext.startActivity(intent);} else {//左屏幕上下滑动SCROLL_VIDEO_LIGHT_INDEX += distanceY;Log.e(TAG, "toponScroll:" + SCROLL_VIDEO_LIGHT_INDEX);double lightIndex = (((double) SCROLL_VIDEO_LIGHT_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * SCROLL_LIGHT_MAX;
// lightIndex = -lightIndex;//取反double newLightIndex = lightIndex + SCREEN_LIGHT_CATCH;if (newLightIndex > SCROLL_LIGHT_MAX) {//说明拖动到末尾SCREEN_LIGHT = SCROLL_LIGHT_MAX;} else if (newLightIndex < 0) {//说明拖动到开头SCREEN_LIGHT = 0;} else {SCREEN_LIGHT = (int) newLightIndex;}// 申请权限后做的操作// 设置系统亮度Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, SCREEN_LIGHT);Log.d(TAG, "屏幕亮度: " + SCREEN_LIGHT);voiceLightsb.setProgress(SCREEN_LIGHT);}}return true;
// break;case SCROLL_FLAG_TOPBOTTOM_RIGHT://右屏幕上下滑动 设置声音SCROLL_VIDEO_VOICE_INDEX += distanceY;
// SCROLL_VIDEO_VOICE_INDEX = (int) distanceY;double voiceIndex = (((double) SCROLL_VIDEO_VOICE_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * SCROLL_VOICE_MAX;
// lightIndex = -lightIndex;//取反if (voiceIndex > 1) {voiceIndex = 1;SCROLL_VIDEO_VOICE_INDEX = 0;} else if (voiceIndex < -1) {voiceIndex = -1;SCROLL_VIDEO_VOICE_INDEX = 0;} else {voiceIndex = 0;}Log.d(TAG, "voiceIndex: " + voiceIndex);double newVoiceIndex = voiceIndex + SCREEN_VOICE;if (newVoiceIndex > SCROLL_VOICE_MAX) {//说明拖动到末尾SCREEN_VOICE = SCROLL_VOICE_MAX;} else if (newVoiceIndex < 0) {//说明拖动到开头SCREEN_VOICE = 0;} else {SCREEN_VOICE = (int) newVoiceIndex;}Log.d(TAG, "结束声音大小: " + SCREEN_VOICE);systemService.setStreamVolume(AudioManager.STREAM_MUSIC, SCREEN_VOICE, AudioManager.FLAG_PLAY_SOUND);voiceLightsb.setProgress(SCREEN_VOICE);return true;
// break;case SCROLL_FLAG_LEFTRIGHT://左右滑动if (Math.abs(distanceY) > 1) {break;}SCROLL_VIDEO_PLAY_INDEX += distanceX;//得到当前视频进度videoIndex = (((double) SCROLL_VIDEO_PLAY_INDEX / (double) SCROLL_VIDEO_SCROLL_RANGE)) * (SCROLL_VIDEO_LENTH * SCROLL_VIDEO_PLAY_RANGE);//说明是进度滑动videoIndex = -videoIndex;//取反newVideoIndex = videoIndex + SCROLL_VIDEO_PLAYING_INDEX_CATCH;
// Log.d("print", "videoIndex: " + videoIndex + "-----newVideoIndex:" + newVideoIndex);if (newVideoIndex > SCROLL_VIDEO_LENTH) {//说明拖动到末尾SCROLL_VIDEO_PLAYING_INDEX = SCROLL_VIDEO_LENTH;} else if (newVideoIndex < 0) {//说明拖动到开头SCROLL_VIDEO_PLAYING_INDEX = 0;} else {SCROLL_VIDEO_PLAYING_INDEX = newVideoIndex;}if ((videoIndex / 1000) > 0f) {//快进indeximg.setImageResource(R.drawable.indeximg_left);} else {//快退indeximg.setImageResource(R.drawable.indeximg_right);}
// Log.d("print", "滑动结束,拖动进度为" + videoIndex / 1000 + "秒");
// Log.d("print", "滑动结束,当前进度为" + SCROLL_VIDEO_PLAYING_INDEX / 1000 + "秒");indextv.setText(parse2TimeStr(SCROLL_VIDEO_PLAYING_INDEX) + "/" + parse2TimeStr(SCROLL_VIDEO_LENTH));return true;
// break;}return false;}@Overridepublic void onLongPress(MotionEvent e) {Log.e(TAG, "onLongPress");}@Overridepublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {Log.e(TAG, "onFling");return false;}}private void showIndexDialog() {indexDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);indexDialog.show();}private void dismissIndexDialog() {if (indexDialog != null) {indexDialog.dismiss();}}public interface OnVideoControlListener {//得到视频总长度double getScrollVideoLenth();//得到视频当前播放位置double getScrollVideoPlayingIndex();//设置视频当前播放位置void setScrollVideoPlayingIndex(double playIndex);//双击屏幕void onDoubleTap();//单击屏幕void onSingleTap();//得到视频控件的大小范围Rect getVideoViewRect();}public void setOnVideoControlListener(OnVideoControlListener videoControlListener) {this.videoControlListener = videoControlListener;}private String parse2TimeStr(double timeStr) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));if (timeStr / 1000 > 3600) {//说明超过一小时simpleDateFormat.applyPattern("HH:mm:ss");} else {//说明没超过一小时simpleDateFormat.applyPattern("mm:ss");}return simpleDateFormat.format(timeStr);}}
使用起来其实也很简单,我们这里自定义了一个控件MyVideoView
假设这个MyVideoView就是我们的视频控件
那么如何让这个控件使用我们的工具类
从而实现手势控制播放进度,声音,亮度的功能呢?
看下面的代码
public class MyVideoView extends View {private VideoGestureControlUtil videoGestureControlUtil;private double playIndex = 0;//视频当前播放位置public MyVideoView(Context context) {this(context, null);}public MyVideoView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public MyVideoView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {videoGestureControlUtil = new VideoGestureControlUtil(context, this);videoGestureControlUtil.setOnVideoControlListener(new VideoGestureControlUtil.OnVideoControlListener() {@Overridepublic double getScrollVideoLenth() {return 36 * 60 * 1000;//36分钟的视频}@Overridepublic double getScrollVideoPlayingIndex() {return playIndex;}@Overridepublic void setScrollVideoPlayingIndex(double playIndex) {MyVideoView.this.playIndex = playIndex;}@Overridepublic void onDoubleTap() {Log.d("print", "onSingleTap: 双击");}@Overridepublic void onSingleTap() {Log.d("print", "onSingleTap: 单击");}@Overridepublic Rect getVideoViewRect() {Rect rect = new Rect();boolean localVisibleRect = getGlobalVisibleRect(rect);if (localVisibleRect) {return rect;}return null;}});}@Overridepublic boolean onTouchEvent(MotionEvent event) {return videoGestureControlUtil.touch(event);}
}
可以看到实现起来其实很简单
核心在onTouchEvent里面
把控件的触摸监听反馈给这个工具类即可
同时需要实现五个接口
这五个接口
需要返回视频的时长,当前播放进度
同时拖动完成后,会返回当前应该播放的进度
单击,双击的监听也都已经实现
这边需要重点介绍getVideoViewRect这个方法
这个方法是用来返回这个控件在屏幕上的位置的
具体可以参考该篇博客
(转载)Android:6种方式让你高效 & 正确地获取View的坐标位置
为什么需要控件把位置传递过去呢?
那是因为我们的工具类里面
已经实现了手指拖动时播放进度、亮度,声音变化时的弹窗
需要根据用户的位置,来调整这个弹窗的位置
当然,也可以去掉这个功能,自己注释掉工具类的dialog代码即可
关于dialog弹窗位置如何设置,可以参考这篇博客或者百度搜索下即可
(转载)Android 关于dialog的显示位置设置
这样,这个视频播放器就实现了通过手势控制播放进度,亮度,声音,以及单击,双击的功能
说完了基本使用
再来说说原理
首先是手势的监听
可以参考这篇博客
(转载)Android GestureDetector详解
接下来分析我们工具类里的具体实现
parse2TimeStr方法主要是对时间进行格式化
方便弹窗的时候,播放进度的文字显示一致
touch方法主要是用来监听手指抬起(MotionEvent.ACTION_UP)
或者手指按住但此时息屏(MotionEvent.ACTION_CANCEL)的情况
这个时候需要关闭弹窗,同时根据拖动的距离,去回调方法
告诉视频播放器当前拖动到的播放进度
核心监听MyGestureListener类
就是对双击,单击,拖动进行处理的地方了
注意,这边将拖动进行了不同状态的划分
分别是
SCROLL_FLAG_RESET 初始状态,也就是手指没按下去的状态
SCROLL_FLAG_TOPBOTTOM_LEFT 左边屏幕上下滑动,控制亮度
SCROLL_FLAG_TOPBOTTOM_RIGHT 右边屏幕上下滑动,控制声音
SCROLL_FLAG_LEFTRIGHT 左右滑动,控制播放进度
手指拖动时会判断状态,抬起后状态复原
另外注意这段代码
if (e1.getX() < FLIP_DISTANCE || e1.getX() > displayMetrics.widthPixels - FLIP_DISTANCE || e1.getY() > displayMetrics.heightPixels - FLIP_DISTANCE) {Log.d(TAG, "onScroll: 无效动作" + e1.getX());//这里主要是处理屏幕边缘滑动的问题,防止和边缘的滑动返回等操作冲突,所以屏蔽了边缘的部分滑动。return true;}
主要来防止在屏幕边缘(比如底部或者两侧)进行拖动,不需要时可以注释掉
dialog的位置设置主要是以下代码,可以根据实际需要进行修改
indexDialogLp.x = ((VideoViewRect.right - VideoViewRect.left) / 2) - (displayMetrics.widthPixels / 2);
indexDialogLp.y = ((VideoViewRect.bottom - VideoViewRect.top) / 2) - (displayMetrics.heightPixels / 2) + VideoViewRect.top;
最后,因为弹窗需要用到一些图片什么的,这里也一并上传
在网盘里下载即可
链接:
https://pan.baidu.com/s/1_E8IeJhXz2jTwdigLGgkaA
提取码:
kkzu
(原创)视频播放器的手势控制工具类相关推荐
- 测试:视频播放器、视频通话、视频类网站 测试点
文章目录 前言 一.视频播放器测试用例 二.视频通话测试用例 三.视频网站测试用例 参考文章 前言 百度和字节面试被问到视频相关的测试用例,作为一个研究方向是视频编解码的人来说,这个问题回答不好,着实 ...
- ( 持续更新,目前含 200+ 工具类 ) DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用。
DevUtils GitHub About ( 持续更新,目前含 200+ 工具类 ) Roadmap DevUtils 是一个 Android 工具库,主要根据不同功能模块,封装快捷使用的工具类及 ...
- 基于NDK、C++、FFmpeg的android视频播放器开发实战-夏曹俊-专题视频课程
基于NDK.C++.FFmpeg的android视频播放器开发实战-1796人已学习 课程介绍 课程包含了对流媒体(拉流)的播放,演示了播放rtmp的香港卫视,支持rtsp摄像头和ht ...
- 视频教程-基于NDK、C++、FFmpeg的android视频播放器开发实战-Android
基于NDK.C++.FFmpeg的android视频播放器开发实战 夏曹俊:南京捷帝科技有限公司创始人,南京大学计算机硕士毕业,有15年c++跨平台项目研发的经验,领导开发过大量的c++虚拟仿真,计算 ...
- Android的媒体播放器——简易视频播放器(详解)
一.案例效果 二.主界面的设计与功能 2.1 案例效果 2.2 布局界面 activity_main.xml 参考代码: <?xml version="1.0" encodi ...
- (持续更新, 目前含100+工具类) DevUtils 是一个 Android 工具库
DevUtils Github About (持续更新, 目前含100+工具类) DevUtils 是一个 Android 工具库, 主要根据不同功能模块,封装快捷使用的工具类及 API 方法调用. ...
- Android Studio——简单视频播放器
1.界面分析 三个界面:主界面,视频列表界面,视频播放界面,如下图所示: 2.实现思路 (1)主界面设计 很简单,就一个背景和一个控件,然后为控件设置点击事件,然后跳转到视频列表界面 activity ...
- 线程工具类 - CountDownLatch(倒计时器)
CountDownLatch官方文档 一.原理 CountDownLatch是一个非常实用的多线程控制工具类.Count Down在英文中意为倒计时,Latch意为门闩,可以简单的将CountDown ...
- Android开发之视频播放器
Android开发之视频播放器 一.效果图 二.build.gradle中导入依赖 三.主布局文件(activity_video) 四.布局文件(video_item) 五.布局文件(activity ...
最新文章
- SAP RETAIL 如何确定自动补货触发的单据类型
- Spring Boot项目(Maven\Gradle)三种启动方式及后台运行详解
- Qt Creator编码
- 关于Vue.use()详解
- node模块加载机制。
- 原好未来CFO罗戎加盟百度 出任百度集团CFO
- TensorFlow基础篇(七)——tf.nn.conv2d()
- 关于mysql的wait_timeout参数 设置不生效的问题【转】
- 谁是应用软件商店急需的外援
- 小米网络信号测试软件,WiFi测速
- 搜狗微信临时链接转换成永久链接
- [深度长文] 996的经济学
- 如何安装PrCC2019
- 网红神盾七号重疾险再创新高,自带住院津贴,还能赔两次!
- Google AppSheet: 无需编程构建零代码应用
- ROS MoveIT1(Noetic)安装总结
- vue下载excel模板
- Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day7】 —— 数据库2(事务)
- 天眼查新方式信息爬取
- java 布尔值的判断