android视频播放器(vitamio)

2024-06-04 21:38

本文主要是介绍android视频播放器(vitamio),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、我们有三种方式来实现视频的播放

1、使用其自带的播放器。指定Action为ACTION_VIEW,Data为Uri,Type为其MIME类型。
2、使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。
3、使用MediaPlayer类和SurfaceView来实现,这种方式很灵活。


这里主要使用了vitamio万能播放器,官网:https://www.vitamio.org/,

vitamio支持格式:mp3/mp4/mkv/avi/3gp/rmvb/mov/flv


简介:1当然最重要的是播放功能,含播放控制(播放、暂停,声音、亮度调整等
           2可以扫描SDcard,用列表展现出来,可以搜索
           3可以播放网络视音频(httprtspmms
网络协议:mms,RTSP(RTP/SDP), HTTP流式传输(progressive streaming),
 HTTPLive Streaming (M3U8), Android 2.1+


其他:1FFmpeg提供软件解码器和多路输出(demuxers)
           2Universalchardet (Mozilla的一个自动检测页面文字编码的程序)   UniversalchardetMozilla的编码检测库。Vitamio使用MPL许可
  Universalchardet的代码来检测字母文本编码。


2、自带播放器实现

Intent intent = new Intent();intent.setDataAndType(Uri.parse("http://data.vod.itc.cn/?rb=1&prot=1&key=jbZhEJhlqlUN-Wj_HEI8BjaVqKNFvDrn&prod=flash&pt=1&new=/86/168/RUXzy059JIvGRrrXQvOkNE.mp4"), "video/*");startActivity(intent);
3、videoView播放器实现

Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/Test_Movie.m4v");  VideoView videoView = (VideoView)this.findViewById(R.id.video_view);  videoView.setMediaController(new MediaController(this));  videoView.setVideoURI(uri);  videoView.start();  videoView.requestFocus();  
4、 vitamio播放器是继承surfaceView实现的,在实现之前先添加vitamio的库,可到官网下载,build.gradle配置

apply plugin: 'com.android.application'android {compileSdkVersion 25buildToolsVersion "25.0.2"defaultConfig {applicationId "com.example.apple.vitamiodemo"minSdkVersion 15targetSdkVersion 25versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}
}dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {exclude group: 'com.android.support', module: 'support-annotations'})compile 'com.android.support:appcompat-v7:25.1.1'testCompile 'junit:junit:4.12'compile project(':vitamio')compile 'com.jakewharton:butterknife:8.4.0'annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
}

5、权限文件实现代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.apple.vitamiodemo"><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.VIBRATE"/><applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".activity.VideoList"android:theme="@style/AppTheme"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name="io.vov.vitamio.activity.InitActivity"android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"android:launchMode="singleTop"android:theme="@android:style/Theme.NoTitleBar"android:windowSoftInputMode="stateAlwaysHidden" /><activityandroid:name=".MainActivity"android:configChanges="keyboardHidden|screenSize|orientation"android:screenOrientation="landscape"android:theme="@style/noAnimation_Theme"/></application></manifest>

6、都准备好后,首先先实现获取本地视频,通过列表显示。

/*** 从本地的sdcard得到数据* <p>* 1、遍历sdcard ,后缀名* <p>* 2、去数据库中获取,内容提供者* <p>* 3、如果是6.0系统需要动态读取sdcand权限*/public void getDataFromLocal() {new Thread() {@Overridepublic void run() {super.run();mediaItems = new ArrayList<>();ContentResolver resolver = getContentResolver();Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;String[] objs = {MediaStore.Video.Media.DISPLAY_NAME,//视屏文件在sdcard的名称MediaStore.Video.Media.DURATION,//视屏总时长MediaStore.Video.Media.SIZE,//视屏的文件大小MediaStore.Video.Media.DATA,//视屏的绝对地址MediaStore.Video.Media.ARTIST,//歌曲的演唱者};Cursor cursor = resolver.query(uri, objs, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {MediaItem mediaItem = new MediaItem();String name = cursor.getString(0);//视频名称mediaItem.setName(name);long duration = cursor.getLong(1);//视频的时长mediaItem.setDuration(duration);long size = cursor.getLong(2);//视频大小mediaItem.setSize(size);String data = cursor.getString(3);//视频播放地址mediaItem.setData(data);String artist = cursor.getString(4);//艺术家mediaItem.setArtist(artist);mediaItems.add(mediaItem);//可以写在上面}cursor.close();}//Handler发消息handler.sendEmptyMessage(10);}}.start();}

adapter布局代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:padding="3dp"android:layout_height="100dp"><ImageViewandroid:layout_marginLeft="8dp"android:id="@+id/iv_icon"android:layout_width="100dp"android:layout_height="100dp"android:src="@drawable/btn_video_play" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_marginLeft="3dp"android:layout_toRightOf="@+id/iv_icon"android:gravity="center_vertical"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="视频名称"android:textColor="#77000000"android:textSize="16sp" /><TextViewandroid:id="@+id/tv_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="20:00"android:textColor="@android:color/black"android:textSize="16sp" /></LinearLayout><TextViewandroid:id="@+id/tv_size"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginRight="3dp"android:layout_marginTop="5dp"android:text="20MB"android:textColor="@android:color/black"android:textSize="16sp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_alignParentBottom="true"android:layout_marginLeft="8dp"android:layout_marginRight="8dp"android:layout_marginTop="8dp"android:background="#44000000" />
</RelativeLayout>

adapter代码实现

package com.example.apple.vitamiodemo.adapter;import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;import com.example.apple.vitamiodemo.R;
import com.example.apple.vitamiodemo.data.MediaItem;
import com.example.apple.vitamiodemo.utils.Utils;import java.text.DecimalFormat;
import java.util.ArrayList;import butterknife.BindView;
import butterknife.ButterKnife;/*** Created by apple on 2017/2/7.** VideoPager Adapter*/public class VideoListAdapter extends BaseAdapter{private boolean isVideo = false;private final Context context;private ArrayList<MediaItem> mediaItems = new ArrayList<>();private Utils utils;public VideoListAdapter(Context context,ArrayList<MediaItem> mediaItem,boolean isVideo) {this.context = context;this.mediaItems = mediaItem;this.isVideo = isVideo;}@Overridepublic int getCount() {return mediaItems.size();}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;utils = new Utils();if (convertView == null) {convertView = View.inflate(context, R.layout.item_video_pager, null);viewHolder = new ViewHolder(convertView);convertView.setTag(viewHolder);}else {viewHolder = (ViewHolder)convertView.getTag();}//根据position得到列表中对应位置的数据MediaItem mediaItem = mediaItems.get(position);viewHolder.tvName.setText(mediaItem.getName());viewHolder.tvSize.setText(FormetFileSize(mediaItem.getSize()));viewHolder.tvTime.setText(utils.stringForTime((int) mediaItem.getDuration()));if (!isVideo){//音频viewHolder.ivIcon.setImageResource(R.mipmap.ic_launcher);}return convertView;}static class ViewHolder {@BindView(R.id.iv_icon)ImageView ivIcon;@BindView(R.id.tv_name)TextView tvName;@BindView(R.id.tv_time)TextView tvTime;@BindView(R.id.tv_size)TextView tvSize;ViewHolder(View view) {ButterKnife.bind(this, view);}}/*** 转换文件大小* @param fileS* @return fileSizeString*/public String FormetFileSize(long fileS) {DecimalFormat df = new DecimalFormat("#.00");String fileSizeString = "";if (fileS < 1024) {fileSizeString = df.format((double) fileS) + "B";} else if (fileS < 1048576) {fileSizeString = df.format((double) fileS / 1024) + "K";} else if (fileS < 1073741824) {fileSizeString = df.format((double) fileS / 1048576) + "M";} else {fileSizeString = df.format((double) fileS / 1073741824) + "G";}return fileSizeString;}
}

完整显示视频数据代码videoList.java

package com.example.apple.vitamiodemo.activity;import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;import com.example.apple.vitamiodemo.MainActivity;
import com.example.apple.vitamiodemo.R;
import com.example.apple.vitamiodemo.adapter.VideoListAdapter;
import com.example.apple.vitamiodemo.data.MediaItem;import java.util.ArrayList;import butterknife.BindView;
import butterknife.ButterKnife;public class VideoList extends AppCompatActivity {@BindView(R.id.listView)ListView listView;@BindView(R.id.tv_nomedia)TextView tvNomedia;@BindView(R.id.pb_loading)ProgressBar pbLoading;private VideoListAdapter videoPagerAdapter;/*** 装数据集合*/private ArrayList<MediaItem> mediaItems;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if (mediaItems != null && mediaItems.size() > 0) {//有数据//设置适配器videoPagerAdapter = new VideoListAdapter(getApplication(),mediaItems,true);listView.setAdapter(videoPagerAdapter);//文本隐藏tvNomedia.setVisibility(View.GONE);} else {//没有数据//文本显示tvNomedia.setVisibility(View.VISIBLE);}//隐藏progresspbLoading.setVisibility(View.GONE);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_video_list);ButterKnife.bind(this);initData();listView.setOnItemClickListener(new MyOnItemClickListener());}private void initData() {getDataFromLocal();}class MyOnItemClickListener implements AdapterView.OnItemClickListener{@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {MediaItem mediaItem = mediaItems.get(position);//调起系统所有播放器--隐式意图
//            Intent intent = new Intent();
//            intent.setDataAndType(Uri.parse(mediaItem.getData()),"video/*");
//            startActivity(intent);//调用自己写的播放器,显示意图
//            Intent intent = new Intent(getActivity(),SystemVideoPlayer.class);
//            intent.setDataAndType(Uri.parse(mediaItem.getData()),"video/*");
//            startActivity(intent);//3、传递列表数据-对象-序列化Intent intent = new Intent(getApplicationContext(),MainActivity.class);Bundle bundle = new Bundle();bundle.putSerializable("videolist",mediaItems);intent.putExtra("position",position);intent.putExtras(bundle);startActivity(intent);}}/*** 从本地的sdcard得到数据* <p>* 1、遍历sdcard ,后缀名* <p>* 2、去数据库中获取,内容提供者* <p>* 3、如果是6.0系统需要动态读取sdcand权限*/public void getDataFromLocal() {new Thread() {@Overridepublic void run() {super.run();mediaItems = new ArrayList<>();ContentResolver resolver = getContentResolver();Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;String[] objs = {MediaStore.Video.Media.DISPLAY_NAME,//视屏文件在sdcard的名称MediaStore.Video.Media.DURATION,//视屏总时长MediaStore.Video.Media.SIZE,//视屏的文件大小MediaStore.Video.Media.DATA,//视屏的绝对地址MediaStore.Video.Media.ARTIST,//歌曲的演唱者};Cursor cursor = resolver.query(uri, objs, null, null, null);if (cursor != null) {while (cursor.moveToNext()) {MediaItem mediaItem = new MediaItem();String name = cursor.getString(0);//视频名称mediaItem.setName(name);long duration = cursor.getLong(1);//视频的时长mediaItem.setDuration(duration);long size = cursor.getLong(2);//视频大小mediaItem.setSize(size);String data = cursor.getString(3);//视频播放地址mediaItem.setData(data);String artist = cursor.getString(4);//艺术家mediaItem.setArtist(artist);mediaItems.add(mediaItem);//可以写在上面}cursor.close();}//Handler发消息handler.sendEmptyMessage(10);}}.start();}
}

7、因为需要设置播放器大小,一次自定义一个view,然后继承vitamio的videoView


package com.example.apple.vitamiodemo.view;import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;import io.vov.vitamio.widget.VideoView;/*** Created by apple on 17/3/6.*/public class MyVideoView extends VideoView{/*** 在代码中创建的时候一般用这个方法* @param context*/public MyVideoView(Context context) {super(context,null);}/*** 当这个类在布局文件的时候,系统通过该构造方法实例化该类* @param context* @param attrs*/public MyVideoView(Context context, AttributeSet attrs) {super(context, attrs,0);}/*** 当需要设置样式的时候调用该方法* @param context* @param attrs* @param defStyleAttr*/public MyVideoView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}/*** 设置视频宽高* @param videoWidth* @param videoHeight*/public void setVideoSize(int videoWidth,int videoHeight){ViewGroup.LayoutParams params = getLayoutParams();params.width = videoWidth;params.height = videoHeight;setLayoutParams(params);//requestLayout();}
}

8、实现布局代码:

<?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:background="@android:color/black"android:gravity="center"><com.example.apple.vitamiodemo.view.MyVideoViewandroid:id="@+id/mVideoView"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:layout_centerVertical="true" /><includeandroid:id="@+id/media_controller"layout="@layout/activity_media_controller" /><includeandroid:id="@+id/ll_buffer"layout="@layout/ll_buffer" /><includeandroid:id="@+id/ll_loading"layout="@layout/ll_loading" /></RelativeLayout>
用动态广播更新电量显示,注册广播

//注册电量广播myReceiver = new MyReceiver();IntentFilter intentFiler = new IntentFilter();//当电量变化的时候发送广播intentFiler.addAction(Intent.ACTION_BATTERY_CHANGED);registerReceiver(myReceiver, intentFiler);
/*** 电量变化广播*/class MyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {int level = intent.getIntExtra("level", 0);//电量 0~100setBattery(level);}}
/*** 电量图标显示** @param level 电量*/public void setBattery(int level) {if (level <= 0) {//电量为0ivBattery.setImageResource(R.drawable.ic_battery_0);} else if (level <= 10) {ivBattery.setImageResource(R.drawable.ic_battery_10);} else if (level <= 20) {ivBattery.setImageResource(R.drawable.ic_battery_20);} else if (level <= 40) {ivBattery.setImageResource(R.drawable.ic_battery_40);} else if (level <= 60) {ivBattery.setImageResource(R.drawable.ic_battery_60);} else if (level <= 80) {ivBattery.setImageResource(R.drawable.ic_battery_80);} else if (level <= 100) {ivBattery.setImageResource(R.drawable.ic_battery_100);} else {ivBattery.setImageResource(R.drawable.ic_battery_100);}}
左边上下滑动改变亮度,右边上下滑动改变音量

 @Overridepublic boolean onTouchEvent(MotionEvent event) {//3、把事件传递给手势识别器detector.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://手指按下//1、按下去startY = event.getY();startX = event.getX();mVol = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//按下获取音量touchRang = Math.min(screenHeight, screenWidth);//获取屏幕最小值即高度handler.removeMessages(HIDE_MEDIA_CONTROLLER);break;case MotionEvent.ACTION_MOVE://移动的纪录相关值float endY = event.getY();float endX = event.getX();float distanceY = endY - startY;if (endX<screenWidth/2){//左边屏幕--调节亮度final double FLING_MIN_DISTANCE = 0.5;final double FLING_MIN_VELOCITY = 0.5;if (startY - endY > FLING_MIN_DISTANCE&& Math.abs(distanceY) > FLING_MIN_VELOCITY) {Log.e(TAG, "up");setBrightness(20);}if (startY - endY < FLING_MIN_DISTANCE&& Math.abs(distanceY) > FLING_MIN_VELOCITY) {Log.e(TAG, "down");setBrightness(-20);}}else {//右边--调节声音Log.e(TAG, "MotionEvent ACTION_MOVE distanceY=" + distanceY);//改变的音量 = (滑动屏幕的距离:总距离)* 音量最大值float delta = (distanceY / touchRang) * maxVoice;//最终的音量 = 原来的 + 改变的int voice = (int) Math.min(Math.max((mVol + delta), 0), maxVoice);//加个距离判断防止误触if ((delta != 0 && distanceY > 5) || (delta != 0 && distanceY < -5)) {isMute = false;//设置非静音updateVoice(voice, false);//Log.e(TAG,"MotionEvent voice"+voice);}}break;case MotionEvent.ACTION_UP:handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);break;}return super.onTouchEvent(event);}/*** 设置屏幕亮度 lp = 0 全暗 ,lp= -1,根据系统设置, lp = 1; 最亮*/private Vibrator vibrator;public void setBrightness(float brightness) {WindowManager.LayoutParams lp = getWindow().getAttributes();// if (lp.screenBrightness <= 0.1) {// return;// }lp.screenBrightness = lp.screenBrightness + brightness / 255.0f;if (lp.screenBrightness > 1) {lp.screenBrightness = 1;vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...vibrator.vibrate(pattern, -1);} else if (lp.screenBrightness < 0.2) {lp.screenBrightness = (float) 0.2;vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...vibrator.vibrate(pattern, -1);}Log.e(TAG, "lp.screenBrightness= " + lp.screenBrightness);getWindow().setAttributes(lp);}

完整实现代码

package com.example.apple.vitamiodemo;import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;import com.example.apple.vitamiodemo.data.MediaItem;
import com.example.apple.vitamiodemo.utils.Utils;
import com.example.apple.vitamiodemo.view.MyVideoView;import java.util.ArrayList;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.Vitamio;public class MainActivity extends Activity {private final String TAG = "MainActivity";//视频进度的更新private static final int PROGRESS = 1;/*** 控制面板显隐*/private static final int HIDE_MEDIA_CONTROLLER = 2;/*** 全屏*/private static final int FULL_SCREEN = 3;/*** 默认屏幕*/private static final int DEFAULT_FULL_SCREEN = 5;/*** 网络速度*/private static final int SHOW_SPEED = 4;@BindView(R.id.mVideoView)MyVideoView mVideoView;@BindView(R.id.tv_name)TextView tvName;@BindView(R.id.iv_battery)ImageView ivBattery;@BindView(R.id.tv_system_time)TextView tvSystemTime;@BindView(R.id.btn_voice)Button btnVoice;@BindView(R.id.seekbar_voice)SeekBar seekbarVoice;@BindView(R.id.btn_switch_player)Button btnSwitchPlayer;@BindView(R.id.ll_top)LinearLayout llTop;@BindView(R.id.tv_current_time)TextView tvCurrentTime;@BindView(R.id.seekbar_video)SeekBar seekbarVideo;@BindView(R.id.tv_duration)TextView tvDuration;@BindView(R.id.btn_exit)Button btnExit;@BindView(R.id.btn_video_pre)Button btnVideoPre;@BindView(R.id.btn_video_start_pause)Button btnVideoStartPause;@BindView(R.id.btn_video_next)Button btnVideoNext;@BindView(R.id.btn_video_siwch_screen)Button btnVideoSiwchScreen;@BindView(R.id.ll_bottom)LinearLayout llBottom;@BindView(R.id.tv_buffer_netSpeed)TextView tvBufferNetSpeed;@BindView(R.id.tv_loading_netSpeed)TextView tvLoadingNetSpeed;private LinearLayout ll_loading;private RelativeLayout media_controller;private LinearLayout ll_buffer;/*** 视频真实宽高*/private int mVideoWidth = 0;private int mVideoHeight = 0;/*** 屏幕宽高*/private int screenWidth = 0;private int screenHeight = 0;/*** 是否全屏*/private boolean isFullScreen = false;/*** 调节声音*/private AudioManager am;/*** 当前声音*/private int currentVolue;/*** 最大音量  0~15*/private int maxVoice;/*** 是否是静音*/private boolean isMute = false;private float startY;private float startX;//屏幕的高private float touchRang;private int mVol;/*** 定义手势识别器*/private GestureDetector detector;/*** 是否显示控制面板*/private boolean isShowMediaController = false;/*** 传进来的视频列表*/private ArrayList<MediaItem> mediaItems;private String path = "http://data.vod.itc.cn/?rb=1&prot=1&key=jbZhEJhlqlUN-Wj_HEI8BjaVqKNFvDrn&prod=flash&pt=1&new=/86/168/RUXzy059JIvGRrrXQvOkNE.mp4";private Uri uri;/*** 是否是网络uri*/private boolean isNetUri;/*** 列表中播放的位置*/private int position;private Utils utils;//是否系统private boolean isUseSystem = true;/*** 上一次播放进度*/private int preCurrentPosition;/*** 监听电量变化广播*/private MyReceiver myReceiver;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case PROGRESS://得到当前的视频的播放进程int currentPosition = (int) mVideoView.getCurrentPosition();//seekbar.setProgress(当前进度)seekbarVideo.setProgress(currentPosition);//更新播放进度tvCurrentTime.setText(utils.stringForTime(currentPosition));//设置系统时间tvSystemTime.setText(utils.getSystemTime());seekbarVideo.setSecondaryProgress(0);//缓存进度的更新if (isNetUri) {//网络地址int buffer = mVideoView.getBufferPercentage();//0~100int totalBuffer = buffer * seekbarVideo.getMax();int secondaryProgress = totalBuffer / 100;seekbarVideo.setSecondaryProgress(secondaryProgress);} else {//本地地址seekbarVideo.setSecondaryProgress(0);}//监听卡if (!isUseSystem && mVideoView.isPlaying()) {if (mVideoView.isPlaying()) {int buffer = currentPosition - preCurrentPosition;if (buffer < 500) {//视频卡了ll_buffer.setVisibility(View.VISIBLE);} else {//视频不卡了ll_buffer.setVisibility(View.GONE);}} else {ll_buffer.setVisibility(View.GONE);}}preCurrentPosition = currentPosition;//每秒更新一次handler.removeMessages(PROGRESS);handler.sendEmptyMessageDelayed(PROGRESS, 1000);break;case SHOW_SPEED://显示网速//得到网速String netSpeed = utils.showNetSpeed(getApplication());tvLoadingNetSpeed.setText("加载中" + netSpeed);tvLoadingNetSpeed.setText("缓存中" + netSpeed);//两秒一次handler.removeMessages(SHOW_SPEED);handler.sendEmptyMessageDelayed(SHOW_SPEED, 2000);break;case HIDE_MEDIA_CONTROLLER://隐藏控制面板hideMediaController();break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Vitamio.isInitialized(this);//vitamio播放器初始化//        if (!LibsChecker.checkVitamioLibs(this))
//            return;setContentView(R.layout.activity_main);ButterKnife.bind(this);initView();initData();getData();setData();mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mediaPlayer) {//加载页面消掉ll_loading.setVisibility(View.GONE);// optional need Vitamio 4.0//   mediaPlayer.setPlaybackSpeed(1.0f);mVideoWidth = mediaPlayer.getVideoWidth();mVideoHeight = mediaPlayer.getVideoHeight();mVideoView.start();//开始播放long duration = mVideoView.getDuration();//得到总时长seekbarVideo.setMax((int) duration);//设置最大值tvDuration.setText(utils.stringForTime((int) duration));//设置视频宽高setVideoType(DEFAULT_FULL_SCREEN);//发消息handler.sendEmptyMessage(PROGRESS);}});mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {showErrorDialog();Toast.makeText(getApplication(), "播放出错!!!", Toast.LENGTH_SHORT).show();return true;}});mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {playNextVideo();}});//视频拖动监听seekbarVideo.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {/*** 当手指滑动的时候,引起seekBar进度变化* @param seekBar* @param progress* @param fromUser 如果是用户引起的是true 不是用户引起的是false*/@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {if (fromUser) {mVideoView.seekTo(progress);}}/*** 当手指触碰的时候回调这个方法* @param seekBar*/@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {handler.removeMessages(HIDE_MEDIA_CONTROLLER);}/*** 当手指离开的时候* @param seekBar*/@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);}});/*** 声音进度条*/seekbarVoice.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {Log.e(TAG, "setOnSeekBarChangeListener progress=" + progress + "fromUser=" + fromUser);if (fromUser) {//更新音量if (progress > 0) {isMute = false;} else {isMute = true;}Log.e(TAG, "updateVoice(progress, isMute);");updateVoice(progress, isMute);}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {}});if (isUseSystem) {//监听视频播放卡,系统apiif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {switch (what) {case MediaPlayer.MEDIA_INFO_BUFFERING_START://视频卡顿了,拖动卡//Toast.makeText(getApplication(),"卡了",Toast.LENGTH_SHORT).show();ll_buffer.setVisibility(View.VISIBLE);break;case MediaPlayer.MEDIA_INFO_BUFFERING_END://视频卡结束了,拖动卡结束ll_buffer.setVisibility(View.GONE);// Toast.makeText(getApplication(),"卡结束了",Toast.LENGTH_SHORT).show();break;}return false;}});}}}@OnClick({R.id.btn_voice, R.id.btn_switch_player, R.id.btn_exit, R.id.btn_video_pre, R.id.btn_video_start_pause, R.id.btn_video_next, R.id.btn_video_siwch_screen})public void onClick(View view) {switch (view.getId()) {case R.id.btn_voice:isMute = !isMute;updateVoice(currentVolue, isMute);break;case R.id.btn_switch_player:break;case R.id.btn_exit:finish();break;case R.id.btn_video_pre:playPreVideo();break;case R.id.btn_video_start_pause:startAndPause();break;case R.id.btn_video_next:playNextVideo();break;case R.id.btn_video_siwch_screen:setFullScreenAndDefault();//设置全屏与默认break;}}/*** 初始化view*/private void initView() {utils = new Utils();ll_loading = (LinearLayout) findViewById(R.id.ll_loading);ll_loading.setVisibility(View.GONE);media_controller = (RelativeLayout) findViewById(R.id.media_controller);ll_buffer = (LinearLayout) findViewById(R.id.ll_buffer);ll_buffer.setVisibility(View.GONE);//开始更新网络速度handler.sendEmptyMessage(SHOW_SPEED);}/*** 初始化数据*/private void initData() {//注册电量广播myReceiver = new MyReceiver();IntentFilter intentFiler = new IntentFilter();//当电量变化的时候发送广播intentFiler.addAction(Intent.ACTION_BATTERY_CHANGED);registerReceiver(myReceiver, intentFiler);/*** 2、实例化手势识别器,并且重写双击,点击和长按*/detector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {/*** 长按* @param e*/@Overridepublic void onLongPress(MotionEvent e) {Log.e(TAG, "onLongPress");//  Toast.makeText(getApplication(),"onLongPress",Toast.LENGTH_SHORT).show();startAndPause();super.onLongPress(e);}/*** 双击* @param e* @return*/@Overridepublic boolean onDoubleTap(MotionEvent e) {Log.e(TAG, "onDoubleTap");setFullScreenAndDefault();// videoView.setVideoSize(500, 500);return super.onDoubleTap(e);}/*** 单击* @param e* @return*/@Overridepublic boolean onSingleTapConfirmed(MotionEvent e) {Log.e(TAG, "onSingleTapConfirmed");if (isShowMediaController) {hideMediaController();//隐藏控制面板//把隐藏消息移除handler.removeMessages(HIDE_MEDIA_CONTROLLER);} else {showMediaController();//显示控制面板//发消息隐藏handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);}//Toast.makeText(getApplication(),"onSingleTapConfirmed",Toast.LENGTH_SHORT).show();return super.onSingleTapConfirmed(e);}});DisplayMetrics displayMetrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);//得到屏幕宽高像素screenWidth = displayMetrics.widthPixels;screenHeight = displayMetrics.heightPixels;//得到音量,设置音量am = (AudioManager) getSystemService(AUDIO_SERVICE);currentVolue = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);maxVoice = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//最大音量seekbarVoice.setMax(maxVoice);Log.e(TAG, "maxVoice===" + maxVoice + " initData currentVolue=" + currentVolue);//设置音量seekbarVoice.setProgress(currentVolue);}/*** 获取数据*/private void getData() {//得到播放地址,视频播放器会被调起并且播放uri = getIntent().getData();//文件夹,图片浏览器,qq空间mediaItems = (ArrayList<MediaItem>) getIntent().getSerializableExtra("videolist");position = getIntent().getIntExtra("position", 0);if (uri != null) {mVideoView.setVideoURI(uri);//设置播放地址,开始播放}}/*** 设置数据*/private void setData() {if (mediaItems != null && mediaItems.size() > 0) {MediaItem mediaItem = mediaItems.get(position);tvName.setText(mediaItem.getName());//显示视频的名字mVideoView.setVideoPath(mediaItem.getData());//设置地址isNetUri = utils.isNetUri(mediaItem.getData());} else if (uri != null) {tvName.setText(uri.toString());//设置视频的名称isNetUri = utils.isNetUri(uri.toString());mVideoView.setVideoURI(uri);//设置播放地址,以Uri的方式设置VideoView播放的视频源,可以是网络Uri或本地Uri。} else {Toast.makeText(this, "没有数据传递过来", Toast.LENGTH_SHORT).show();}setButtonState();}/*** 播放出错弹的提示dialog*/private void showErrorDialog() {AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("提示");builder.setMessage("抱歉,无法播放该视频");builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {finish();}});builder.show();}/*** 播放上一个视频*/private void playPreVideo() {if (mediaItems != null && mediaItems.size() > 0) {//播放下一个position--;if (position >= 0) {ll_loading.setVisibility(View.VISIBLE);MediaItem mediaItem = mediaItems.get(position);tvName.setText(mediaItem.getName());isNetUri = utils.isNetUri(mediaItem.getData());mVideoView.setVideoPath(mediaItem.getData());//设置按钮状态setButtonState();}} else if (uri != null) {//上一个和下一个按钮设置不可操作 //设置按钮状态setButtonState();}}/*** 开始播放和暂停播放*/private void startAndPause() {if (mVideoView.isPlaying()) {//视频播放-设置暂停mVideoView.pause();//按钮状态设置播放btnVideoStartPause.setBackgroundResource(R.drawable.btn_video_pause_selector2);Log.e(TAG,"pause");} else {//视频播放mVideoView.start();//按钮暂停btnVideoStartPause.setBackgroundResource(R.drawable.btn_video_pause_selector);Log.e(TAG,"start");}}/*** 播放下一个*/private void playNextVideo() {if (mediaItems != null && mediaItems.size() > 0) {//播放下一个position++;if (position < mediaItems.size()) {ll_loading.setVisibility(View.VISIBLE);MediaItem mediaItem = mediaItems.get(position);tvName.setText(mediaItem.getName());//设置视频名称isNetUri = utils.isNetUri(mediaItem.getData());//判断是否是网络视频mVideoView.setVideoPath(mediaItem.getData());//设置按钮状态setButtonState();}} else if (uri != null) {//上一个和下一个按钮设置不可操作//设置按钮状态setButtonState();}}/*** 全屏与默认切换*/private void setFullScreenAndDefault() {if (isFullScreen) {//默认setVideoType(DEFAULT_FULL_SCREEN);} else {//全屏setVideoType(FULL_SCREEN);}}private void setVideoType(int defaultFullScreen) {switch (defaultFullScreen) {case FULL_SCREEN://全屏//1、设置视频画面大小 屏幕大小mVideoView.setVideoSize(screenWidth, screenHeight);//2、设置按钮状态btnVideoSiwchScreen.setBackgroundResource(R.drawable.btn_video_siwch_screen_default_selector);//3、全屏isFullScreen = true;break;case DEFAULT_FULL_SCREEN://默认//1、设置视频画面大小//屏幕宽高int height = screenHeight;int width = screenWidth;// for compatibility, we adjust size based on aspect ratioif (mVideoWidth * height < width * mVideoHeight) {//Log.i("@@@", "image too wide, correcting");width = height * mVideoWidth / mVideoHeight;} else if (mVideoWidth * height > width * mVideoHeight) {//Log.i("@@@", "image too tall, correcting");height = width * mVideoHeight / mVideoWidth;}mVideoView.setVideoSize(width, height);//2、设置按钮状态btnVideoSiwchScreen.setBackgroundResource(R.drawable.btn_video_siwch_screen_selector);isFullScreen = false;break;}}/*** 设置按钮状态*/private void setButtonState() {if (mediaItems != null && mediaItems.size() > 0) {if (mediaItems.size() == 1) {//两个按钮设置灰色setEnableFalse();} else if (mediaItems.size() == 2) {if (position == 0) {//两个按钮设置灰色btnVideoPre.setBackgroundResource(R.drawable.video_pre_gray);btnVideoPre.setEnabled(false);btnVideoNext.setBackgroundResource(R.drawable.btn_video_next_selector);btnVideoNext.setEnabled(true);} else if (position == 1) {//下一个按钮不可操作btnVideoNext.setBackgroundResource(R.drawable.video_next_btn_bg);btnVideoNext.setEnabled(false);btnVideoPre.setBackgroundResource(R.drawable.btn_video_pre_selector);btnVideoPre.setEnabled(true);}} else {if (position == 0) {//两个按钮设置灰色btnVideoPre.setBackgroundResource(R.drawable.video_pre_gray);btnVideoPre.setEnabled(false);} else if (position == mediaItems.size() - 1) {//下一个按钮不可操作btnVideoNext.setBackgroundResource(R.drawable.video_next_btn_bg);btnVideoNext.setEnabled(false);} else {btnVideoPre.setBackgroundResource(R.drawable.btn_video_pre_selector);btnVideoPre.setEnabled(true);btnVideoNext.setBackgroundResource(R.drawable.btn_video_next_selector);btnVideoNext.setEnabled(true);}}} else if (uri != null) {//两个按钮设置灰色setEnableFalse();}}/*** //两个按钮设置灰色*/private void setEnableFalse() {btnVideoPre.setBackgroundResource(R.drawable.video_pre_gray);btnVideoPre.setEnabled(false);btnVideoNext.setBackgroundResource(R.drawable.video_next_btn_bg);btnVideoNext.setEnabled(false);}/*** 显示控制面板*/private void showMediaController() {media_controller.setVisibility(View.VISIBLE);isShowMediaController = true;}/*** 隐藏控制面板*/private void hideMediaController() {media_controller.setVisibility(View.GONE);isShowMediaController = false;}@Overrideprotected void onDestroy() {super.onDestroy();//移除所有消息handler.removeCallbacksAndMessages(null);if (myReceiver != null) {unregisterReceiver(myReceiver);myReceiver = null;}}@Overridepublic boolean onTouchEvent(MotionEvent event) {//3、把事件传递给手势识别器detector.onTouchEvent(event);switch (event.getAction()) {case MotionEvent.ACTION_DOWN://手指按下//1、按下去startY = event.getY();startX = event.getX();mVol = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);//按下获取音量touchRang = Math.min(screenHeight, screenWidth);//获取屏幕最小值即高度handler.removeMessages(HIDE_MEDIA_CONTROLLER);break;case MotionEvent.ACTION_MOVE://移动的纪录相关值float endY = event.getY();float endX = event.getX();float distanceY = endY - startY;if (endX<screenWidth/2){//左边屏幕--调节亮度final double FLING_MIN_DISTANCE = 0.5;final double FLING_MIN_VELOCITY = 0.5;if (startY - endY > FLING_MIN_DISTANCE&& Math.abs(distanceY) > FLING_MIN_VELOCITY) {Log.e(TAG, "up");setBrightness(20);}if (startY - endY < FLING_MIN_DISTANCE&& Math.abs(distanceY) > FLING_MIN_VELOCITY) {Log.e(TAG, "down");setBrightness(-20);}}else {//右边--调节声音Log.e(TAG, "MotionEvent ACTION_MOVE distanceY=" + distanceY);//改变的音量 = (滑动屏幕的距离:总距离)* 音量最大值float delta = (distanceY / touchRang) * maxVoice;//最终的音量 = 原来的 + 改变的int voice = (int) Math.min(Math.max((mVol + delta), 0), maxVoice);//加个距离判断防止误触if ((delta != 0 && distanceY > 5) || (delta != 0 && distanceY < -5)) {isMute = false;//设置非静音updateVoice(voice, false);//Log.e(TAG,"MotionEvent voice"+voice);}}break;case MotionEvent.ACTION_UP:handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);break;}return super.onTouchEvent(event);}/*** 设置屏幕亮度 lp = 0 全暗 ,lp= -1,根据系统设置, lp = 1; 最亮*/private Vibrator vibrator;public void setBrightness(float brightness) {WindowManager.LayoutParams lp = getWindow().getAttributes();// if (lp.screenBrightness <= 0.1) {// return;// }lp.screenBrightness = lp.screenBrightness + brightness / 255.0f;if (lp.screenBrightness > 1) {lp.screenBrightness = 1;vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...vibrator.vibrate(pattern, -1);} else if (lp.screenBrightness < 0.2) {lp.screenBrightness = (float) 0.2;vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);long[] pattern = { 10, 200 }; // OFF/ON/OFF/ON...vibrator.vibrate(pattern, -1);}Log.e(TAG, "lp.screenBrightness= " + lp.screenBrightness);getWindow().setAttributes(lp);}/*** 物理按键控制** @param keyCode* @param event* @return*/@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {Log.e(TAG, "onKeyDown currentVolue = " + currentVolue);if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {currentVolue--;updateVoice(currentVolue, false);handler.removeMessages(HIDE_MEDIA_CONTROLLER);handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);return true;} else if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {currentVolue++;updateVoice(currentVolue, false);handler.removeMessages(HIDE_MEDIA_CONTROLLER);handler.sendEmptyMessageDelayed(HIDE_MEDIA_CONTROLLER, 4000);return true;}return super.onKeyDown(keyCode, event);}/*** 设置音量大小** @param progress 进度条值* @param isMute   是否静音*/private void updateVoice(int progress, boolean isMute) {if (isMute) {am.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);//1调系统seekbarVoice.setProgress(0);} else {am.setStreamVolume(AudioManager.STREAM_MUSIC, progress, 0);//1调系统seekbarVoice.setProgress(progress);currentVolue = progress;}Log.e(TAG, "updateVoice currentVolue=" + currentVolue);}/*** 电量图标显示** @param level 电量*/public void setBattery(int level) {if (level <= 0) {//电量为0ivBattery.setImageResource(R.drawable.ic_battery_0);} else if (level <= 10) {ivBattery.setImageResource(R.drawable.ic_battery_10);} else if (level <= 20) {ivBattery.setImageResource(R.drawable.ic_battery_20);} else if (level <= 40) {ivBattery.setImageResource(R.drawable.ic_battery_40);} else if (level <= 60) {ivBattery.setImageResource(R.drawable.ic_battery_60);} else if (level <= 80) {ivBattery.setImageResource(R.drawable.ic_battery_80);} else if (level <= 100) {ivBattery.setImageResource(R.drawable.ic_battery_100);} else {ivBattery.setImageResource(R.drawable.ic_battery_100);}}/*** 电量变化广播*/class MyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {int level = intent.getIntExtra("level", 0);//电量 0~100setBattery(level);}}
}
实现效果图:



最后代码下载地址:https://github.com/ningxingxing/vitamio

注意:在小米手机上测试出现了bug,无法播放,需要在加载布局之前添加下面代码

// ~~~ 检测Vitamio是否解压解码包
if (!LibsChecker.checkVitamioLibs(this))return;





















这篇关于android视频播放器(vitamio)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1031192

相关文章

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后