├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── Java-WebSocket-1.3.2-leancloud.jar │ ├── avoscloud-push-v3.14.5.jar │ ├── avoscloud-sdk-v3.14.5.jar │ ├── fastjson.jar │ ├── okhttp-2.6.0-leancloud.jar │ ├── okio-1.6.0-leancloud.jar │ └── protobuf-java-2.6.1.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── zgh │ │ └── livedemo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── zgh │ │ │ └── livedemo │ │ │ ├── Config.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MyApp.java │ │ │ └── view │ │ │ ├── BarrageItem.java │ │ │ ├── BarrageView.java │ │ │ └── MyVideoView.java │ └── res │ │ ├── layout │ │ ├── activity_login.xml │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── zgh │ └── livedemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── vitamio ├── AndroidManifest.xml ├── README.md ├── build.gradle ├── libs ├── armeabi-v7a │ └── libvinit.so ├── armeabi │ └── libvinit.so ├── mips │ └── libvinit.so └── x86 │ └── libvinit.so ├── proguard-project.txt ├── project.properties ├── res ├── drawable-xhdpi │ ├── mediacontroller_pause.png │ ├── mediacontroller_play.png │ ├── scrubber_control_disabled_holo.png │ ├── scrubber_control_focused_holo.png │ ├── scrubber_control_normal_holo.png │ ├── scrubber_control_pressed_holo.png │ ├── scrubber_primary_holo.9.png │ ├── scrubber_secondary_holo.9.png │ └── scrubber_track_holo_dark.9.png ├── drawable │ ├── mediacontroller_button.xml │ ├── scrubber_control_selector_holo.xml │ └── scrubber_progress_horizontal_holo_dark.xml ├── layout │ └── mediacontroller.xml ├── raw │ └── libarm.so └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml └── src └── io └── vov └── vitamio ├── EGL.java ├── LibsChecker.java ├── MediaFile.java ├── MediaFormat.java ├── MediaMetadataRetriever.java ├── MediaPlayer.java ├── MediaScanner.java ├── MediaScannerClient.java ├── Metadata.java ├── ThumbnailUtils.java ├── VIntent.java ├── Vitamio.java ├── VitamioLicense.java ├── activity └── InitActivity.java ├── provider ├── MediaStore.java └── MiniThumbFile.java ├── utils ├── Base64.java ├── CPU.java ├── ContextUtils.java ├── Crypto.java ├── Device.java ├── FileUtils.java ├── IOUtils.java ├── Log.java ├── ScreenResolution.java └── StringUtils.java └── widget ├── CenterLayout.java ├── MediaController.java ├── OutlineTextView.java └── VideoView.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.zgh.livedemo" 9 | minSdkVersion 15 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.4.0' 26 | compile project(':vitamio') 27 | compile files('libs/avoscloud-push-v3.14.5.jar') 28 | compile files('libs/protobuf-java-2.6.1.jar') 29 | compile files('libs/Java-WebSocket-1.3.2-leancloud.jar') 30 | compile files('libs/okhttp-2.6.0-leancloud.jar') 31 | compile files('libs/avoscloud-sdk-v3.14.5.jar') 32 | compile files('libs/fastjson.jar') 33 | compile files('libs/okio-1.6.0-leancloud.jar') 34 | } 35 | -------------------------------------------------------------------------------- /app/libs/Java-WebSocket-1.3.2-leancloud.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/Java-WebSocket-1.3.2-leancloud.jar -------------------------------------------------------------------------------- /app/libs/avoscloud-push-v3.14.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/avoscloud-push-v3.14.5.jar -------------------------------------------------------------------------------- /app/libs/avoscloud-sdk-v3.14.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/avoscloud-sdk-v3.14.5.jar -------------------------------------------------------------------------------- /app/libs/fastjson.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/fastjson.jar -------------------------------------------------------------------------------- /app/libs/okhttp-2.6.0-leancloud.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/okhttp-2.6.0-leancloud.jar -------------------------------------------------------------------------------- /app/libs/okio-1.6.0-leancloud.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/okio-1.6.0-leancloud.jar -------------------------------------------------------------------------------- /app/libs/protobuf-java-2.6.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhuguohui/LiveDemo/f895e3ec6bab065e9a92668b47344d0257419b2e/app/libs/protobuf-java-2.6.1.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in F:\work\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/zgh/livedemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/Config.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo; 2 | 3 | /** 4 | * Created by zhuguohui on 2016/9/20. 5 | */ 6 | public interface Config { 7 | /** 8 | * learnCloud APP_ID 9 | */ 10 | String APP_ID = ""; 11 | /** 12 | * learnCloud APP_KEY 13 | */ 14 | String APP_KEY = ""; 15 | /** 16 | * learnCloud 聊天室ID 17 | */ 18 | String CONVERSATION_ID = ""; 19 | /** 20 | * rtmp 视频地址 21 | */ 22 | String VIDEO_URL = ""; 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.text.TextUtils; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | import android.widget.Toast; 10 | 11 | import com.avos.avoscloud.im.v2.AVIMClient; 12 | import com.avos.avoscloud.im.v2.AVIMException; 13 | import com.avos.avoscloud.im.v2.callback.AVIMClientCallback; 14 | 15 | public class LoginActivity extends AppCompatActivity { 16 | EditText et_name; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_login); 22 | et_name = (EditText) findViewById(R.id.et_name); 23 | findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() { 24 | @Override 25 | public void onClick(View v) { 26 | String name = et_name.getText().toString(); 27 | if (TextUtils.isEmpty(name)) { 28 | Toast.makeText(LoginActivity.this, "登录名不能为空", Toast.LENGTH_SHORT).show(); 29 | return; 30 | } 31 | login(name); 32 | } 33 | }); 34 | } 35 | 36 | 37 | public void login(String name) { 38 | //Jerry登录 39 | AVIMClient jerry = AVIMClient.getInstance(name); 40 | jerry.open(new AVIMClientCallback() { 41 | 42 | @Override 43 | public void done(AVIMClient client, AVIMException e) { 44 | if (e == null) { 45 | Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show(); 46 | MyApp.mClient = client; 47 | startActivity(new Intent(LoginActivity.this, MainActivity.class)); 48 | } else { 49 | Toast.makeText(LoginActivity.this, "登录失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); 50 | } 51 | } 52 | }); 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.pm.ActivityInfo; 6 | import android.content.res.Configuration; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.text.TextUtils; 10 | import android.text.method.KeyListener; 11 | import android.view.KeyEvent; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.view.WindowManager; 15 | import android.widget.BaseAdapter; 16 | import android.widget.EditText; 17 | import android.widget.FrameLayout; 18 | import android.widget.LinearLayout; 19 | import android.widget.RelativeLayout; 20 | import android.widget.TextView; 21 | import android.widget.Toast; 22 | 23 | import com.avos.avoscloud.im.v2.AVIMClient; 24 | import com.avos.avoscloud.im.v2.AVIMConversation; 25 | import com.avos.avoscloud.im.v2.AVIMException; 26 | import com.avos.avoscloud.im.v2.AVIMMessage; 27 | import com.avos.avoscloud.im.v2.AVIMMessageHandler; 28 | import com.avos.avoscloud.im.v2.AVIMMessageManager; 29 | import com.avos.avoscloud.im.v2.callback.AVIMClientCallback; 30 | import com.avos.avoscloud.im.v2.callback.AVIMConversationCallback; 31 | import com.avos.avoscloud.im.v2.callback.AVIMConversationCreatedCallback; 32 | import com.avos.avoscloud.im.v2.messages.AVIMTextMessage; 33 | import com.zgh.livedemo.view.BarrageView; 34 | import com.zgh.livedemo.view.MyVideoView; 35 | 36 | import java.util.Arrays; 37 | 38 | import io.vov.vitamio.LibsChecker; 39 | import io.vov.vitamio.MediaPlayer; 40 | import io.vov.vitamio.utils.Log; 41 | import io.vov.vitamio.widget.MediaController; 42 | import io.vov.vitamio.widget.VideoView; 43 | 44 | public class MainActivity extends Activity { 45 | 46 | private MyVideoView mVideoView; 47 | private String path; 48 | private AVIMConversation conv; 49 | private RoomMessageHandler roomMessageHandler; 50 | private EditText et_send; 51 | BarrageView barrageView; 52 | private LinearLayout ll_room; 53 | private View layout_video, layout_loading; 54 | private TextView tv_present; 55 | 56 | @Override 57 | protected void onCreate(Bundle savedInstanceState) { 58 | super.onCreate(savedInstanceState); 59 | if (!LibsChecker.checkVitamioLibs(this)) 60 | return; 61 | setContentView(R.layout.activity_main); 62 | layout_loading = findViewById(R.id.layout_loading); 63 | layout_video = findViewById(R.id.layout_video); 64 | tv_present = (TextView) findViewById(R.id.tv_present); 65 | findViewById(R.id.btn_fullscreen).setOnClickListener(new View.OnClickListener() { 66 | @Override 67 | public void onClick(View v) { 68 | 69 | fullScreen(); 70 | } 71 | }); 72 | barrageView = (BarrageView) findViewById(R.id.containerView); 73 | ll_room = (LinearLayout) findViewById(R.id.ll_room); 74 | et_send = (EditText) findViewById(R.id.et_send); 75 | mVideoView = (MyVideoView) findViewById(R.id.vitamio_videoView); 76 | path = Config.VIDEO_URL; 77 | mVideoView.setVideoPath(path); 78 | MediaController mediaController = (MediaController) findViewById(R.id.mediacontroller); 79 | // mediaController.setAnchorView(mVideoView); 80 | mVideoView.setMediaController(mediaController); 81 | mVideoView.requestFocus(); 82 | mVideoView.setOnInfoListener(new MediaPlayer.OnInfoListener() { 83 | public boolean onInfo(MediaPlayer mp, int what, int extra) { 84 | if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { 85 | layout_loading.setVisibility(View.VISIBLE); 86 | android.util.Log.i("zzz", "onStart"); 87 | 88 | } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { 89 | //此接口每次回调完START就回调END,若不加上判断就会出现缓冲图标一闪一闪的卡顿现象 90 | android.util.Log.i("zzz", "onEnd"); 91 | layout_loading.setVisibility(View.GONE); 92 | // mp.start(); 93 | mVideoView.start(); 94 | } 95 | return true; 96 | } 97 | }); 98 | mVideoView.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() { 99 | @Override 100 | public void onBufferingUpdate(MediaPlayer mp, int percent) { 101 | if (!mp.isPlaying()) { 102 | layout_loading.setVisibility(View.VISIBLE); 103 | tv_present.setText("正在缓冲" + percent + "%"); 104 | } else { 105 | layout_loading.setVisibility(View.GONE); 106 | } 107 | 108 | } 109 | }); 110 | mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { 111 | @Override 112 | public void onPrepared(MediaPlayer mediaPlayer) { 113 | mediaPlayer.setPlaybackSpeed(1.0f); 114 | } 115 | }); 116 | mVideoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { 117 | @Override 118 | public boolean onError(MediaPlayer mp, int what, int extra) { 119 | android.util.Log.i("zzz", "onError what=" + what + " extra=" + extra); 120 | tv_present.setText("加载失败"); 121 | return true; 122 | } 123 | }); 124 | 125 | roomMessageHandler = new RoomMessageHandler(); 126 | join(); 127 | 128 | et_send.setOnKeyListener(new MyKeyListener()); 129 | 130 | } 131 | 132 | 133 | private class MyKeyListener implements View.OnKeyListener { 134 | @Override 135 | public boolean onKey(View v, int keyCode, KeyEvent event) { 136 | if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { 137 | sendMsg(); 138 | return true; 139 | } 140 | return false; 141 | } 142 | } 143 | 144 | private void sendMsg() { 145 | final String msg = et_send.getText().toString(); 146 | if (TextUtils.isEmpty(msg)) { 147 | Toast.makeText(this, "内容不能为空", Toast.LENGTH_SHORT).show(); 148 | return; 149 | } 150 | if (conv != null) { 151 | AVIMTextMessage message = new AVIMTextMessage(); 152 | message.setText(msg); 153 | conv.sendMessage(message, new AVIMConversationCallback() { 154 | @Override 155 | public void done(AVIMException e) { 156 | if (e == null) { 157 | et_send.setText(""); 158 | addMsg(msg); 159 | } else { 160 | Toast.makeText(MainActivity.this, "发送失败", Toast.LENGTH_SHORT).show(); 161 | } 162 | } 163 | }); 164 | } 165 | } 166 | 167 | private void join() { 168 | MyApp.mClient.open(new AVIMClientCallback() { 169 | 170 | @Override 171 | public void done(AVIMClient client, AVIMException e) { 172 | if (e == null) { 173 | //登录成功 174 | conv = client.getConversation(Config.CONVERSATION_ID); 175 | conv.join(new AVIMConversationCallback() { 176 | @Override 177 | public void done(AVIMException e) { 178 | if (e == null) { 179 | //加入成功 180 | Toast.makeText(MainActivity.this, "加入聊天室成功", Toast.LENGTH_SHORT).show(); 181 | et_send.setEnabled(true); 182 | } else { 183 | Toast.makeText(MainActivity.this, "加入聊天室失败:" + e.getMessage(), Toast.LENGTH_SHORT).show(); 184 | et_send.setEnabled(false); 185 | android.util.Log.i("zzz", "加入聊天室失败 :" + e.getMessage()); 186 | } 187 | } 188 | }); 189 | } 190 | } 191 | }); 192 | } 193 | 194 | @Override 195 | protected void onResume() { 196 | super.onResume(); 197 | AVIMMessageManager.registerMessageHandler(AVIMTextMessage.class, roomMessageHandler); 198 | } 199 | 200 | @Override 201 | protected void onPause() { 202 | super.onPause(); 203 | AVIMMessageManager.unregisterMessageHandler(AVIMTextMessage.class, roomMessageHandler); 204 | } 205 | 206 | public class RoomMessageHandler extends AVIMMessageHandler { 207 | //接收到消息后的处理逻辑 208 | @Override 209 | public void onMessage(AVIMMessage message, AVIMConversation conversation, AVIMClient client) { 210 | if (message instanceof AVIMTextMessage) { 211 | String info = ((AVIMTextMessage) message).getText(); 212 | //添加消息到屏幕 213 | addMsg(info); 214 | } 215 | } 216 | 217 | } 218 | 219 | private void addMsg(String msg) { 220 | TextView textView = new TextView(MainActivity.this); 221 | textView.setText(msg); 222 | ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 223 | params.setMargins(5, 10, 5, 10); 224 | textView.setLayoutParams(params); 225 | ll_room.addView(textView, 0); 226 | barrageView.addMessage(msg); 227 | } 228 | 229 | private void fullScreen() { 230 | if (isScreenOriatationPortrait(this)) {// 当屏幕是竖屏时 231 | full(true); 232 | // 点击后变横屏 233 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);// 设置当前activity为横屏 234 | // 当横屏时 把除了视频以外的都隐藏 235 | //隐藏其他组件的代码 236 | ll_room.setVisibility(View.GONE); 237 | et_send.setVisibility(View.GONE); 238 | int width = getResources().getDisplayMetrics().widthPixels; 239 | int height = getResources().getDisplayMetrics().heightPixels; 240 | layout_video.setLayoutParams(new LinearLayout.LayoutParams(height, width)); 241 | mVideoView.setLayoutParams(new RelativeLayout.LayoutParams(height, width)); 242 | 243 | 244 | } else { 245 | full(false); 246 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);// 设置当前activity为竖屏 247 | //显示其他组件 248 | ll_room.setVisibility(View.VISIBLE); 249 | et_send.setVisibility(View.VISIBLE); 250 | int width = getResources().getDisplayMetrics().heightPixels; 251 | int height = (int) (width * 9.0 / 16); 252 | layout_video.setLayoutParams(new LinearLayout.LayoutParams(width, height)); 253 | mVideoView.setLayoutParams(new RelativeLayout.LayoutParams(width, height)); 254 | 255 | } 256 | } 257 | 258 | //动态隐藏状态栏 259 | private void full(boolean enable) { 260 | if (enable) { 261 | WindowManager.LayoutParams lp = getWindow().getAttributes(); 262 | lp.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 263 | getWindow().setAttributes(lp); 264 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 265 | } else { 266 | WindowManager.LayoutParams attr = getWindow().getAttributes(); 267 | attr.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); 268 | getWindow().setAttributes(attr); 269 | getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 270 | } 271 | } 272 | 273 | 274 | /** 275 | * 返回当前屏幕是否为竖屏。 276 | * 277 | * @param context 278 | * @return 当且仅当当前屏幕为竖屏时返回true, 否则返回false。 279 | */ 280 | public static boolean isScreenOriatationPortrait(Context context) { 281 | return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; 282 | 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/MyApp.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.avos.avoscloud.AVOSCloud; 7 | import com.avos.avoscloud.im.v2.AVIMClient; 8 | import com.avos.avoscloud.im.v2.AVIMConversation; 9 | import com.avos.avoscloud.im.v2.AVIMMessage; 10 | import com.avos.avoscloud.im.v2.AVIMMessageHandler; 11 | import com.avos.avoscloud.im.v2.AVIMMessageManager; 12 | import com.avos.avoscloud.im.v2.messages.AVIMTextMessage; 13 | 14 | /** 15 | * Created by zhuguohui on 2016/9/14. 16 | */ 17 | public class MyApp extends Application { 18 | public static AVIMClient mClient; 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | AVOSCloud.initialize(this, Config.APP_ID,Config.APP_KEY); 24 | 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/view/BarrageItem.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo.view; 2 | 3 | import android.widget.TextView; 4 | 5 | /** 6 | * Created by zhuguohui on 2016/9/11 0011. 7 | */ 8 | public class BarrageItem { 9 | public TextView textView; 10 | public int textColor; 11 | public String text; 12 | public int textSize; 13 | public int moveSpeed;//移动速度 14 | public int verticalPos;//垂直方向显示的位置 15 | public int textMeasuredWidth;//字体显示占据的宽度 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/view/BarrageView.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.Rect; 6 | import android.os.Handler; 7 | import android.os.Message; 8 | import android.text.TextPaint; 9 | import android.util.AttributeSet; 10 | import android.view.animation.AccelerateDecelerateInterpolator; 11 | import android.view.animation.Animation; 12 | import android.view.animation.TranslateAnimation; 13 | import android.widget.RelativeLayout; 14 | import android.widget.TextView; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Random; 19 | 20 | /** 21 | * Created by lixueyong on 16/2/19. 22 | */ 23 | public class BarrageView extends RelativeLayout { 24 | private Context mContext; 25 | private BarrageHandler mHandler = new BarrageHandler(); 26 | private Random random = new Random(System.currentTimeMillis()); 27 | private static final long BARRAGE_GAP_MIN_DURATION = 1000;//两个弹幕的最小间隔时间 28 | private static final long BARRAGE_GAP_MAX_DURATION = 2000;//两个弹幕的最大间隔时间 29 | private int maxSpeed = 10000;//速度,ms 30 | private int minSpeed = 5000;//速度,ms 31 | private int maxSize = 30;//文字大小,dp 32 | private int minSize = 15;//文字大小,dp 33 | 34 | private int totalHeight = 0; 35 | private int lineHeight = 0;//每一行弹幕的高度 36 | private int totalLine = 0;//弹幕的行数 37 | private List messageList = new ArrayList<>(); 38 | // private String[] itemText = {"是否需要帮忙", "what are you 弄啥来", "哈哈哈哈哈哈哈", "抢占沙发。。。。。。", "************", "是否需要帮忙", 39 | // "我不会轻易的狗带", "嘿嘿", "这是我见过的最长长长长长长长长长长长的评论"}; 40 | private int textCount; 41 | // private List itemList = new ArrayList(); 42 | 43 | public BarrageView(Context context) { 44 | this(context, null); 45 | } 46 | 47 | public BarrageView(Context context, AttributeSet attrs) { 48 | this(context, attrs, 0); 49 | } 50 | 51 | public BarrageView(Context context, AttributeSet attrs, int defStyleAttr) { 52 | super(context, attrs, defStyleAttr); 53 | mContext = context; 54 | init(); 55 | } 56 | 57 | private void init() { 58 | // textCount = itemText.length; 59 | 60 | int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random()); 61 | mHandler.sendEmptyMessageDelayed(0, duration); 62 | } 63 | 64 | public void addMessage(String message) { 65 | messageList.add(message); 66 | } 67 | 68 | @Override 69 | public void onWindowFocusChanged(boolean hasWindowFocus) { 70 | super.onWindowFocusChanged(hasWindowFocus); 71 | totalHeight = getMeasuredHeight(); 72 | lineHeight = getLineHeight(); 73 | totalLine = totalHeight / lineHeight; 74 | } 75 | 76 | private void generateItem() { 77 | if (messageList.size() > 0) { 78 | BarrageItem item = new BarrageItem(); 79 | String tx = messageList.remove(0); 80 | int sz = (int) (minSize + (maxSize - minSize) * Math.random()); 81 | item.textView = new TextView(mContext); 82 | item.textView.setText(tx); 83 | item.textView.setTextSize(sz); 84 | item.textView.setTextColor(Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256))); 85 | item.textMeasuredWidth = (int) getTextWidth(item, tx, sz); 86 | item.moveSpeed = (int) (minSpeed + (maxSpeed - minSpeed) * Math.random()); 87 | if (totalLine == 0) { 88 | totalHeight = getMeasuredHeight(); 89 | lineHeight = getLineHeight(); 90 | totalLine = totalHeight / lineHeight; 91 | } 92 | item.verticalPos = random.nextInt(totalLine) * lineHeight; 93 | // itemList.add(item); 94 | showBarrageItem(item); 95 | } 96 | } 97 | 98 | private void showBarrageItem(final BarrageItem item) { 99 | 100 | int leftMargin = this.getRight() - this.getLeft() - this.getPaddingLeft(); 101 | 102 | LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 103 | params.addRule(RelativeLayout.ALIGN_PARENT_TOP); 104 | params.topMargin = item.verticalPos; 105 | this.addView(item.textView, params); 106 | Animation anim = generateTranslateAnim(item, leftMargin); 107 | anim.setAnimationListener(new Animation.AnimationListener() { 108 | @Override 109 | public void onAnimationStart(Animation animation) { 110 | 111 | } 112 | 113 | @Override 114 | public void onAnimationEnd(Animation animation) { 115 | item.textView.clearAnimation(); 116 | BarrageView.this.removeView(item.textView); 117 | } 118 | 119 | @Override 120 | public void onAnimationRepeat(Animation animation) { 121 | 122 | } 123 | }); 124 | item.textView.startAnimation(anim); 125 | } 126 | 127 | private TranslateAnimation generateTranslateAnim(BarrageItem item, int leftMargin) { 128 | TranslateAnimation anim = new TranslateAnimation(leftMargin, -item.textMeasuredWidth, 0, 0); 129 | anim.setDuration(item.moveSpeed); 130 | anim.setInterpolator(new AccelerateDecelerateInterpolator()); 131 | anim.setFillAfter(true); 132 | return anim; 133 | } 134 | 135 | /** 136 | * 计算TextView中字符串的长度 137 | * 138 | * @param text 要计算的字符串 139 | * @param Size 字体大小 140 | * @return TextView中字符串的长度 141 | */ 142 | public float getTextWidth(BarrageItem item, String text, float Size) { 143 | Rect bounds = new Rect(); 144 | TextPaint paint; 145 | paint = item.textView.getPaint(); 146 | paint.getTextBounds(text, 0, text.length(), bounds); 147 | return bounds.width(); 148 | } 149 | 150 | /** 151 | * 获得每一行弹幕的最大高度 152 | * 153 | * @return 154 | */ 155 | private int getLineHeight() { 156 | /* BarrageItem item = new BarrageItem(); 157 | String tx = itemText[0]; 158 | item.textView = new TextView(mContext); 159 | item.textView.setText(tx); 160 | item.textView.setTextSize(maxSize); 161 | 162 | Rect bounds = new Rect(); 163 | TextPaint paint; 164 | paint = item.textView.getPaint(); 165 | paint.getTextBounds(tx, 0, tx.length(), bounds); 166 | return bounds.height();*/ 167 | return 50; 168 | } 169 | 170 | class BarrageHandler extends Handler { 171 | @Override 172 | public void handleMessage(Message msg) { 173 | super.handleMessage(msg); 174 | generateItem(); 175 | //每个弹幕产生的间隔时间随机 176 | int duration = (int) ((BARRAGE_GAP_MAX_DURATION - BARRAGE_GAP_MIN_DURATION) * Math.random()); 177 | this.sendEmptyMessageDelayed(0, duration); 178 | } 179 | } 180 | 181 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zgh/livedemo/view/MyVideoView.java: -------------------------------------------------------------------------------- 1 | package com.zgh.livedemo.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import io.vov.vitamio.widget.VideoView; 7 | 8 | /** 9 | * Created by zhuguohui on 2016/9/19. 10 | */ 11 | public class MyVideoView extends VideoView { 12 | public MyVideoView(Context context) { 13 | super(context); 14 | } 15 | 16 | public MyVideoView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public MyVideoView(Context context, AttributeSet attrs, int defStyle) { 21 | super(context, attrs, defStyle); 22 | } 23 | 24 | @Override 25 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 26 | int width = getDefaultSize(0, widthMeasureSpec); 27 | int height = getDefaultSize(0, heightMeasureSpec); 28 | setMeasuredDimension(width, height); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 |