├── .classpath ├── .project ├── AndroidManifest.xml ├── README.md ├── ic_launcher-web.png ├── libs └── android-support-v4.jar ├── proguard-project.txt ├── project.properties ├── res ├── anim │ ├── option_entry_from_bottom.xml │ ├── option_entry_from_top.xml │ ├── option_leave_from_bottom.xml │ └── option_leave_from_top.xml ├── drawable-hdpi │ ├── ic_launcher.png │ ├── line_point.png │ ├── ling.png │ ├── top_btnback.png │ ├── video_btn_down.png │ ├── video_btn_on.png │ ├── video_line01.png │ └── video_line02.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── drawable │ └── seekbar.xml ├── layout │ ├── activity_main.xml │ └── vv.xml ├── menu │ └── main.xml ├── values-sw600dp │ └── dimens.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── com └── jit └── video ├── DensityUtil.java ├── FullScreenVideoView.java ├── LightnessController.java ├── MainActivity.java ├── VolumnController.java └── VolumnView.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainActivity 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | AndroidVideoView 2 | ================ 3 | 4 | 5 | ## 功能介绍 6 | * Android自定义全屏VideoView,仿主流视频客户端,左右滑屏快退快进 7 | * 上下滑屏,手指在左半屏时,调整亮度 8 | * 上下滑屏,手指在右半屏时,调整音量 9 | * 横竖屏无缝自动切换 10 | 11 | ## 扩展 12 | 如果你有好的实现或者功能扩展,请提交Pull Request。 13 | 如果你有好的想法,请在我的微信公众账号留言,我来实现你的想法。 14 | 15 | ## 交流 16 | * 微信公众账号:Android干货分享(ID:android_share) 17 | * 技术交流QQ群:318588906,欢迎大家加群 18 | * [开源中国博客](http://my.oschina.net/jack1900/blog) 19 | * 邮件(jack_1900@163.com) 20 | -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/ic_launcher-web.png -------------------------------------------------------------------------------- /libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/libs/android-support-v4.jar -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | -------------------------------------------------------------------------------- /res/anim/option_entry_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | -------------------------------------------------------------------------------- /res/anim/option_entry_from_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | -------------------------------------------------------------------------------- /res/anim/option_leave_from_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | -------------------------------------------------------------------------------- /res/anim/option_leave_from_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 15 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-hdpi/line_point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/line_point.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/ling.png -------------------------------------------------------------------------------- /res/drawable-hdpi/top_btnback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/top_btnback.png -------------------------------------------------------------------------------- /res/drawable-hdpi/video_btn_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/video_btn_down.png -------------------------------------------------------------------------------- /res/drawable-hdpi/video_btn_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/video_btn_on.png -------------------------------------------------------------------------------- /res/drawable-hdpi/video_line01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/video_line01.png -------------------------------------------------------------------------------- /res/drawable-hdpi/video_line02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-hdpi/video_line02.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackCho/AndroidVideoView/dde4805cf7a589ea3afe9b56545d86dc9c81fd52/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable/seekbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 17 | 18 | 23 | 24 | 33 | 34 | 35 | 36 | 42 | 43 | 50 | 51 | 62 | 63 | 74 | 75 | 89 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /res/layout/vv.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FullVideoDemo 5 | Settings 6 | Hello world! 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/com/jit/video/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import android.content.Context; 4 | 5 | public class DensityUtil { 6 | 7 | public static final float getHeightInPx(Context context) { 8 | final float height = context.getResources().getDisplayMetrics().heightPixels; 9 | return height; 10 | } 11 | 12 | public static final float getWidthInPx(Context context) { 13 | final float width = context.getResources().getDisplayMetrics().widthPixels; 14 | return width; 15 | } 16 | 17 | public static final int getHeightInDp(Context context) { 18 | final float height = context.getResources().getDisplayMetrics().heightPixels; 19 | int heightInDp = px2dip(context, height); 20 | return heightInDp; 21 | } 22 | 23 | public static final int getWidthInDp(Context context) { 24 | final float height = context.getResources().getDisplayMetrics().heightPixels; 25 | int widthInDp = px2dip(context, height); 26 | return widthInDp; 27 | } 28 | 29 | public static int dip2px(Context context, float dpValue) { 30 | final float scale = context.getResources().getDisplayMetrics().density; 31 | return (int) (dpValue * scale + 0.5f); 32 | } 33 | 34 | public static int px2dip(Context context, float pxValue) { 35 | final float scale = context.getResources().getDisplayMetrics().density; 36 | return (int) (pxValue / scale + 0.5f); 37 | } 38 | 39 | public static int px2sp(Context context, float pxValue) { 40 | final float scale = context.getResources().getDisplayMetrics().density; 41 | return (int) (pxValue / scale + 0.5f); 42 | } 43 | 44 | public static int sp2px(Context context, float spValue) { 45 | final float scale = context.getResources().getDisplayMetrics().density; 46 | return (int) (spValue * scale + 0.5f); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/com/jit/video/FullScreenVideoView.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.VideoView; 6 | 7 | /** 8 | * 自动全屏的VideoView 9 | */ 10 | public class FullScreenVideoView extends VideoView { 11 | 12 | private int videoWidth; 13 | private int videoHeight; 14 | 15 | public FullScreenVideoView(Context context) { 16 | super(context); 17 | } 18 | 19 | public FullScreenVideoView(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | } 22 | 23 | public FullScreenVideoView(Context context, AttributeSet attrs, int defStyle) { 24 | super(context, attrs, defStyle); 25 | } 26 | 27 | @Override 28 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 29 | int width = getDefaultSize(videoWidth, widthMeasureSpec); 30 | int height = getDefaultSize(videoHeight, heightMeasureSpec); 31 | if (videoWidth > 0 && videoHeight > 0) { 32 | if (videoWidth * height > width * videoHeight) { 33 | height = width * videoHeight / videoWidth; 34 | } else if (videoWidth * height < width * videoHeight) { 35 | width = height * videoWidth / videoHeight; 36 | } 37 | } 38 | setMeasuredDimension(width, height); 39 | } 40 | 41 | public int getVideoWidth() { 42 | return videoWidth; 43 | } 44 | 45 | public void setVideoWidth(int videoWidth) { 46 | this.videoWidth = videoWidth; 47 | } 48 | 49 | public int getVideoHeight() { 50 | return videoHeight; 51 | } 52 | 53 | public void setVideoHeight(int videoHeight) { 54 | this.videoHeight = videoHeight; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/com/jit/video/LightnessController.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import android.app.Activity; 4 | import android.content.ContentResolver; 5 | import android.provider.Settings; 6 | import android.view.WindowManager; 7 | import android.widget.Toast; 8 | import android.provider.Settings.System; 9 | 10 | public class LightnessController { 11 | 12 | // 判断是否开启了自动亮度调节 13 | public static boolean isAutoBrightness(Activity act) { 14 | boolean automicBrightness = false; 15 | ContentResolver aContentResolver = act.getContentResolver(); 16 | try { 17 | automicBrightness = Settings.System.getInt(aContentResolver, 18 | Settings.System.SCREEN_BRIGHTNESS_MODE) == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; 19 | } catch (Exception e) { 20 | Toast.makeText(act, "无法获取亮度", Toast.LENGTH_SHORT).show(); 21 | } 22 | return automicBrightness; 23 | } 24 | 25 | // 改变亮度 26 | public static void setLightness(Activity act, int value) { 27 | try { 28 | System.putInt(act.getContentResolver(), System.SCREEN_BRIGHTNESS, value); 29 | WindowManager.LayoutParams lp = act.getWindow().getAttributes(); 30 | lp.screenBrightness = (value <= 0 ? 1 : value) / 255f; 31 | act.getWindow().setAttributes(lp); 32 | } catch (Exception e) { 33 | Toast.makeText(act, "无法改变亮度", Toast.LENGTH_SHORT).show(); 34 | } 35 | } 36 | 37 | // 获取亮度 38 | public static int getLightness(Activity act) { 39 | return System.getInt(act.getContentResolver(), System.SCREEN_BRIGHTNESS, -1); 40 | } 41 | 42 | // 停止自动亮度调节 43 | public static void stopAutoBrightness(Activity activity) { 44 | Settings.System.putInt(activity.getContentResolver(), 45 | Settings.System.SCREEN_BRIGHTNESS_MODE, 46 | Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); 47 | } 48 | 49 | // 开启亮度自动调节 50 | public static void startAutoBrightness(Activity activity) { 51 | Settings.System.putInt(activity.getContentResolver(), 52 | Settings.System.SCREEN_BRIGHTNESS_MODE, 53 | Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/com/jit/video/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import java.text.DateFormat; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | import java.util.Timer; 7 | import java.util.TimerTask; 8 | 9 | import android.annotation.SuppressLint; 10 | import android.app.Activity; 11 | import android.content.Context; 12 | import android.content.res.Configuration; 13 | import android.media.AudioManager; 14 | import android.media.MediaPlayer; 15 | import android.media.MediaPlayer.OnCompletionListener; 16 | import android.media.MediaPlayer.OnPreparedListener; 17 | import android.os.Bundle; 18 | import android.os.Handler; 19 | import android.os.Message; 20 | import android.view.MotionEvent; 21 | import android.view.View; 22 | import android.view.View.OnClickListener; 23 | import android.view.View.OnTouchListener; 24 | import android.view.animation.Animation; 25 | import android.view.animation.Animation.AnimationListener; 26 | import android.view.animation.AnimationUtils; 27 | import android.widget.ImageView; 28 | import android.widget.SeekBar; 29 | import android.widget.SeekBar.OnSeekBarChangeListener; 30 | import android.widget.TextView; 31 | 32 | public class MainActivity extends Activity implements OnClickListener { 33 | 34 | // 自定义VideoView 35 | private FullScreenVideoView mVideo; 36 | 37 | // 头部View 38 | private View mTopView; 39 | 40 | // 底部View 41 | private View mBottomView; 42 | // 视频播放拖动条 43 | private SeekBar mSeekBar; 44 | private ImageView mPlay; 45 | private TextView mPlayTime; 46 | private TextView mDurationTime; 47 | 48 | // 音频管理器 49 | private AudioManager mAudioManager; 50 | 51 | // 屏幕宽高 52 | private float width; 53 | private float height; 54 | 55 | // 视频播放时间 56 | private int playTime; 57 | 58 | private String videoUrl = "http://www.ydtsystem.com/CardImage/21/video/20140305/20140305124807_37734.mp4"; 59 | // 自动隐藏顶部和底部View的时间 60 | private static final int HIDE_TIME = 5000; 61 | 62 | // 声音调节Toast 63 | private VolumnController volumnController; 64 | 65 | // 原始屏幕亮度 66 | private int orginalLight; 67 | 68 | @Override 69 | protected void onCreate(Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_main); 72 | volumnController = new VolumnController(this); 73 | mVideo = (FullScreenVideoView) findViewById(R.id.videoview); 74 | mPlayTime = (TextView) findViewById(R.id.play_time); 75 | mDurationTime = (TextView) findViewById(R.id.total_time); 76 | mPlay = (ImageView) findViewById(R.id.play_btn); 77 | mSeekBar = (SeekBar) findViewById(R.id.seekbar); 78 | mTopView = findViewById(R.id.top_layout); 79 | mBottomView = findViewById(R.id.bottom_layout); 80 | 81 | mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 82 | 83 | width = DensityUtil.getWidthInPx(this); 84 | height = DensityUtil.getHeightInPx(this); 85 | threshold = DensityUtil.dip2px(this, 18); 86 | 87 | orginalLight = LightnessController.getLightness(this); 88 | 89 | mPlay.setOnClickListener(this); 90 | mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener); 91 | 92 | playVideo(); 93 | } 94 | 95 | @Override 96 | public void onConfigurationChanged(Configuration newConfig) { 97 | if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 98 | height = DensityUtil.getWidthInPx(this); 99 | width = DensityUtil.getHeightInPx(this); 100 | } else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { 101 | width = DensityUtil.getWidthInPx(this); 102 | height = DensityUtil.getHeightInPx(this); 103 | } 104 | super.onConfigurationChanged(newConfig); 105 | } 106 | 107 | @Override 108 | protected void onPause() { 109 | super.onPause(); 110 | LightnessController.setLightness(this, orginalLight); 111 | } 112 | 113 | private OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() { 114 | 115 | @Override 116 | public void onStopTrackingTouch(SeekBar seekBar) { 117 | mHandler.postDelayed(hideRunnable, HIDE_TIME); 118 | } 119 | 120 | @Override 121 | public void onStartTrackingTouch(SeekBar seekBar) { 122 | mHandler.removeCallbacks(hideRunnable); 123 | } 124 | 125 | @Override 126 | public void onProgressChanged(SeekBar seekBar, int progress, 127 | boolean fromUser) { 128 | if (fromUser) { 129 | int time = progress * mVideo.getDuration() / 100; 130 | mVideo.seekTo(time); 131 | } 132 | } 133 | }; 134 | 135 | private void backward(float delataX) { 136 | int current = mVideo.getCurrentPosition(); 137 | int backwardTime = (int) (delataX / width * mVideo.getDuration()); 138 | int currentTime = current - backwardTime; 139 | mVideo.seekTo(currentTime); 140 | mSeekBar.setProgress(currentTime * 100 / mVideo.getDuration()); 141 | mPlayTime.setText(formatTime(currentTime)); 142 | } 143 | 144 | private void forward(float delataX) { 145 | int current = mVideo.getCurrentPosition(); 146 | int forwardTime = (int) (delataX / width * mVideo.getDuration()); 147 | int currentTime = current + forwardTime; 148 | mVideo.seekTo(currentTime); 149 | mSeekBar.setProgress(currentTime * 100 / mVideo.getDuration()); 150 | mPlayTime.setText(formatTime(currentTime)); 151 | } 152 | 153 | private void volumeDown(float delatY) { 154 | int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 155 | int current = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 156 | int down = (int) (delatY / height * max * 3); 157 | int volume = Math.max(current - down, 0); 158 | mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 159 | int transformatVolume = volume * 100 / max; 160 | volumnController.show(transformatVolume); 161 | } 162 | 163 | private void volumeUp(float delatY) { 164 | int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); 165 | int current = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); 166 | int up = (int) ((delatY / height) * max * 3); 167 | int volume = Math.min(current + up, max); 168 | mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); 169 | int transformatVolume = volume * 100 / max; 170 | volumnController.show(transformatVolume); 171 | } 172 | 173 | private void lightDown(float delatY) { 174 | int down = (int) (delatY / height * 255 * 3); 175 | int transformatLight = LightnessController.getLightness(this) - down; 176 | LightnessController.setLightness(this, transformatLight); 177 | } 178 | 179 | private void lightUp(float delatY) { 180 | int up = (int) (delatY / height * 255 * 3); 181 | int transformatLight = LightnessController.getLightness(this) + up; 182 | LightnessController.setLightness(this, transformatLight); 183 | } 184 | 185 | @Override 186 | protected void onDestroy() { 187 | super.onDestroy(); 188 | mHandler.removeMessages(0); 189 | mHandler.removeCallbacksAndMessages(null); 190 | } 191 | 192 | @SuppressLint("HandlerLeak") 193 | private Handler mHandler = new Handler() { 194 | 195 | @Override 196 | public void handleMessage(Message msg) { 197 | super.handleMessage(msg); 198 | switch (msg.what) { 199 | case 1: 200 | if (mVideo.getCurrentPosition() > 0) { 201 | mPlayTime.setText(formatTime(mVideo.getCurrentPosition())); 202 | int progress = mVideo.getCurrentPosition() * 100 / mVideo.getDuration(); 203 | mSeekBar.setProgress(progress); 204 | if (mVideo.getCurrentPosition() > mVideo.getDuration() - 100) { 205 | mPlayTime.setText("00:00"); 206 | mSeekBar.setProgress(0); 207 | } 208 | mSeekBar.setSecondaryProgress(mVideo.getBufferPercentage()); 209 | } else { 210 | mPlayTime.setText("00:00"); 211 | mSeekBar.setProgress(0); 212 | } 213 | 214 | break; 215 | case 2: 216 | showOrHide(); 217 | break; 218 | 219 | default: 220 | break; 221 | } 222 | } 223 | }; 224 | 225 | private void playVideo() { 226 | mVideo.setVideoPath(videoUrl); 227 | mVideo.requestFocus(); 228 | mVideo.setOnPreparedListener(new OnPreparedListener() { 229 | @Override 230 | public void onPrepared(MediaPlayer mp) { 231 | mVideo.setVideoWidth(mp.getVideoWidth()); 232 | mVideo.setVideoHeight(mp.getVideoHeight()); 233 | 234 | mVideo.start(); 235 | if (playTime != 0) { 236 | mVideo.seekTo(playTime); 237 | } 238 | 239 | mHandler.removeCallbacks(hideRunnable); 240 | mHandler.postDelayed(hideRunnable, HIDE_TIME); 241 | mDurationTime.setText(formatTime(mVideo.getDuration())); 242 | Timer timer = new Timer(); 243 | timer.schedule(new TimerTask() { 244 | 245 | @Override 246 | public void run() { 247 | mHandler.sendEmptyMessage(1); 248 | } 249 | }, 0, 1000); 250 | } 251 | }); 252 | mVideo.setOnCompletionListener(new OnCompletionListener() { 253 | @Override 254 | public void onCompletion(MediaPlayer mp) { 255 | mPlay.setImageResource(R.drawable.video_btn_down); 256 | mPlayTime.setText("00:00"); 257 | mSeekBar.setProgress(0); 258 | } 259 | }); 260 | mVideo.setOnTouchListener(mTouchListener); 261 | } 262 | 263 | private Runnable hideRunnable = new Runnable() { 264 | 265 | @Override 266 | public void run() { 267 | showOrHide(); 268 | } 269 | }; 270 | 271 | @SuppressLint("SimpleDateFormat") 272 | private String formatTime(long time) { 273 | DateFormat formatter = new SimpleDateFormat("mm:ss"); 274 | return formatter.format(new Date(time)); 275 | } 276 | 277 | private float mLastMotionX; 278 | private float mLastMotionY; 279 | private int startX; 280 | private int startY; 281 | private int threshold; 282 | private boolean isClick = true; 283 | 284 | private OnTouchListener mTouchListener = new OnTouchListener() { 285 | 286 | @Override 287 | public boolean onTouch(View v, MotionEvent event) { 288 | final float x = event.getX(); 289 | final float y = event.getY(); 290 | 291 | switch (event.getAction()) { 292 | case MotionEvent.ACTION_DOWN: 293 | mLastMotionX = x; 294 | mLastMotionY = y; 295 | startX = (int) x; 296 | startY = (int) y; 297 | break; 298 | case MotionEvent.ACTION_MOVE: 299 | float deltaX = x - mLastMotionX; 300 | float deltaY = y - mLastMotionY; 301 | float absDeltaX = Math.abs(deltaX); 302 | float absDeltaY = Math.abs(deltaY); 303 | // 声音调节标识 304 | boolean isAdjustAudio = false; 305 | if (absDeltaX > threshold && absDeltaY > threshold) { 306 | if (absDeltaX < absDeltaY) { 307 | isAdjustAudio = true; 308 | } else { 309 | isAdjustAudio = false; 310 | } 311 | } else if (absDeltaX < threshold && absDeltaY > threshold) { 312 | isAdjustAudio = true; 313 | } else if (absDeltaX > threshold && absDeltaY < threshold) { 314 | isAdjustAudio = false; 315 | } else { 316 | return true; 317 | } 318 | if (isAdjustAudio) { 319 | if (x < width / 2) { 320 | if (deltaY > 0) { 321 | lightDown(absDeltaY); 322 | } else if (deltaY < 0) { 323 | lightUp(absDeltaY); 324 | } 325 | } else { 326 | if (deltaY > 0) { 327 | volumeDown(absDeltaY); 328 | } else if (deltaY < 0) { 329 | volumeUp(absDeltaY); 330 | } 331 | } 332 | 333 | } else { 334 | if (deltaX > 0) { 335 | forward(absDeltaX); 336 | } else if (deltaX < 0) { 337 | backward(absDeltaX); 338 | } 339 | } 340 | mLastMotionX = x; 341 | mLastMotionY = y; 342 | break; 343 | case MotionEvent.ACTION_UP: 344 | if (Math.abs(x - startX) > threshold 345 | || Math.abs(y - startY) > threshold) { 346 | isClick = false; 347 | } 348 | mLastMotionX = 0; 349 | mLastMotionY = 0; 350 | startX = (int) 0; 351 | if (isClick) { 352 | showOrHide(); 353 | } 354 | isClick = true; 355 | break; 356 | 357 | default: 358 | break; 359 | } 360 | return true; 361 | } 362 | 363 | }; 364 | 365 | @Override 366 | public void onClick(View v) { 367 | switch (v.getId()) { 368 | case R.id.play_btn: 369 | if (mVideo.isPlaying()) { 370 | mVideo.pause(); 371 | mPlay.setImageResource(R.drawable.video_btn_down); 372 | } else { 373 | mVideo.start(); 374 | mPlay.setImageResource(R.drawable.video_btn_on); 375 | } 376 | break; 377 | default: 378 | break; 379 | } 380 | } 381 | 382 | private void showOrHide() { 383 | if (mTopView.getVisibility() == View.VISIBLE) { 384 | mTopView.clearAnimation(); 385 | Animation animation = AnimationUtils.loadAnimation(this, 386 | R.anim.option_leave_from_top); 387 | animation.setAnimationListener(new AnimationImp() { 388 | @Override 389 | public void onAnimationEnd(Animation animation) { 390 | super.onAnimationEnd(animation); 391 | mTopView.setVisibility(View.GONE); 392 | } 393 | }); 394 | mTopView.startAnimation(animation); 395 | 396 | mBottomView.clearAnimation(); 397 | Animation animation1 = AnimationUtils.loadAnimation(this, 398 | R.anim.option_leave_from_bottom); 399 | animation1.setAnimationListener(new AnimationImp() { 400 | @Override 401 | public void onAnimationEnd(Animation animation) { 402 | super.onAnimationEnd(animation); 403 | mBottomView.setVisibility(View.GONE); 404 | } 405 | }); 406 | mBottomView.startAnimation(animation1); 407 | } else { 408 | mTopView.setVisibility(View.VISIBLE); 409 | mTopView.clearAnimation(); 410 | Animation animation = AnimationUtils.loadAnimation(this, 411 | R.anim.option_entry_from_top); 412 | mTopView.startAnimation(animation); 413 | 414 | mBottomView.setVisibility(View.VISIBLE); 415 | mBottomView.clearAnimation(); 416 | Animation animation1 = AnimationUtils.loadAnimation(this, 417 | R.anim.option_entry_from_bottom); 418 | mBottomView.startAnimation(animation1); 419 | mHandler.removeCallbacks(hideRunnable); 420 | mHandler.postDelayed(hideRunnable, HIDE_TIME); 421 | } 422 | } 423 | 424 | private class AnimationImp implements AnimationListener { 425 | 426 | @Override 427 | public void onAnimationEnd(Animation animation) { 428 | 429 | } 430 | 431 | @Override 432 | public void onAnimationRepeat(Animation animation) { 433 | } 434 | 435 | @Override 436 | public void onAnimationStart(Animation animation) { 437 | } 438 | 439 | } 440 | 441 | } 442 | -------------------------------------------------------------------------------- /src/com/jit/video/VolumnController.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import android.content.Context; 4 | import android.view.Gravity; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | public class VolumnController { 10 | private Toast t; 11 | private VolumnView tv; 12 | 13 | private Context context; 14 | 15 | public VolumnController(Context context) { 16 | this.context = context; 17 | } 18 | 19 | public void show(float progress) { 20 | if (t == null) { 21 | t = new Toast(context); 22 | View layout = LayoutInflater.from(context).inflate(R.layout.vv, null); 23 | tv = (VolumnView) layout.findViewById(R.id.volumnView); 24 | t.setView(layout); 25 | t.setGravity(Gravity.BOTTOM, 0, 100); 26 | t.setDuration(Toast.LENGTH_SHORT); 27 | } 28 | tv.setProgress(progress); 29 | t.show(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/com/jit/video/VolumnView.java: -------------------------------------------------------------------------------- 1 | package com.jit.video; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Paint.Style; 10 | import android.graphics.RectF; 11 | import android.util.AttributeSet; 12 | import android.view.View; 13 | 14 | /** 15 | * 仿小米声音调节环形进度条 16 | */ 17 | public class VolumnView extends View { 18 | 19 | // 半径 20 | float r1 = 0; 21 | float r2 = 0; 22 | float r3 = 0; 23 | // 外圆宽度 24 | float w1 = 15; 25 | // 内圆宽度 26 | float w2 = 30; 27 | Paint paint; 28 | 29 | // 进度 30 | float progress = 0; 31 | 32 | Bitmap bitmap; 33 | 34 | RectF oval; 35 | 36 | public VolumnView(Context context, AttributeSet attrs, int defStyle) { 37 | super(context, attrs, defStyle); 38 | init(context); 39 | } 40 | 41 | public VolumnView(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | init(context); 44 | } 45 | 46 | public VolumnView(Context context) { 47 | super(context); 48 | init(context); 49 | } 50 | 51 | void init(Context context) { 52 | paint = new Paint(Paint.ANTI_ALIAS_FLAG); 53 | paint.setStyle(Style.STROKE); 54 | bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ling); 55 | } 56 | 57 | @Override 58 | protected void onDraw(Canvas canvas) { 59 | float cx = getMeasuredWidth() / 2; 60 | float cy = getMeasuredHeight() / 2; 61 | r1 = cx - w1 / 2; 62 | r2 = cx - w1 / 2 - w2 / 2; 63 | r3 = cx - w1 / 2 - w2; 64 | 65 | // 绘制外圆 66 | paint.setStrokeWidth(w1); 67 | paint.setColor(Color.parseColor("#454547")); 68 | canvas.drawCircle(cx, cy, r1, paint); 69 | 70 | // 绘制中间圆环 71 | paint.setColor(Color.parseColor("#747476")); 72 | paint.setStrokeWidth(w2); 73 | canvas.drawCircle(cx, cy, r2, paint); 74 | 75 | // 绘制内圆 76 | paint.setColor(Color.parseColor("#464648")); 77 | paint.setStyle(Style.FILL); 78 | canvas.drawCircle(cx, cy, r3, paint); 79 | 80 | // 绘制中间的图片 81 | canvas.drawBitmap(bitmap, cx - bitmap.getWidth() / 2, 82 | cx - bitmap.getHeight() / 2, paint); 83 | 84 | // 绘制文本 85 | paint.setColor(Color.WHITE); 86 | paint.setStrokeWidth(0); 87 | paint.setTextSize(40); 88 | float textWidth = paint.measureText("铃声"); // 测量字体宽度,我们需要根据字体的宽度设置在圆环中间 89 | 90 | canvas.drawText("铃声", cx - textWidth / 2, cx + bitmap.getHeight() / 2 91 | + 40, paint); 92 | 93 | // 绘制进度 94 | paint.setStyle(Style.STROKE); 95 | paint.setStrokeWidth(w2); 96 | paint.setColor(Color.WHITE); 97 | if (oval == null) { 98 | oval = new RectF(cx - r2, cy - r2, cx + r2, cy + r2); // 用于定义的圆弧的形状和大小的界限 99 | } 100 | canvas.drawArc(oval, 270, 360 * progress / 100, false, paint); 101 | 102 | super.onDraw(canvas); 103 | } 104 | 105 | /** 106 | * 设置进度 107 | * 108 | * @param progress 109 | * 范围(0-100) 110 | */ 111 | public void setProgress(float progress) { 112 | this.progress = progress; 113 | if (this.progress >= 100) 114 | this.progress = 100; 115 | if (this.progress <= 0) 116 | this.progress = 0; 117 | postInvalidate(); 118 | } 119 | } 120 | --------------------------------------------------------------------------------