안 드 로 이 드 버들붕어 생방송 탄막 효과

17659 단어 android탄막
전에 어떤 친구 가 제 공식 번호 에서 생방송 과 같은 스크린 기능 을 어떻게 실현 해 야 하 는 지 물 어 본 적 이 있 습 니 다.지금 은 생방송 업계 가 정말 뜨 겁 습 니 다.크 고 작은 회사 들 이 생방송 분야 에 발 을 들 여 놓 아야 합 니 다.버들붕어 로 말하자면 지금 은 천 방 의 싸움 입 니 다.그리고 탄막 은 생방송 기능 에서 가장 중요 한 기능 중 하나 입 니 다.그러면 오늘 저 는 여러분 을 데 리 고 간단 한 안 드 로 이 드 엔 드 탄막 효 과 를 실현 하 겠 습 니 다.
분석 하 다.
우선 버들붕어 의 탄막 효 과 를 살 펴 보 자.아래 그림 과 같다.

이것 은 Dota 2 게임 생방송 의 인터페이스 입 니 다.우 리 는 게임 인터페이스 위 에 많은 탄막 이 있 고 생방송 을 보 는 관중 들 이 바로 이곳 에서 토론 을 하 는 것 을 볼 수 있 습 니 다.
그렇다면 이런 인 터 페 이 스 는 어떻게 이 루어 져 야 할 까?사실은 복잡 하지 않 습 니 다.우 리 는 먼저 레이아웃 에 게임 화면 을 표시 하 는 View 를 설치 한 다음 에 게임 화면 위 에 탄막 을 표시 하 는 View 를 덮어 쓰 면 됩 니 다.탄막 의 View 는 완전히 투명 하 게 만들어 야 한다.그러면 게임 인터페이스의 위 에 덮어 도 게임 의 정상 적 인 관람 에 영향 을 주지 않 고 누군가가 탄막 메 시 지 를 보 낼 때 만 탄막 의 View 위 에 메 시 지 를 그리 면 된다.원리 설명도 아래 와 같다.

그러나 우 리 는 탄막 을 볼 수 있 는 것 외 에 도 탄막 을 보 낼 수 있어 야 하기 때문에 탄막 의 View 위 에 조작 인터페이스의 View 를 다시 덮어 야 한다.그리고 우 리 는 조작 인터페이스 에서 탄막 을 보 내 고 선물 을 줄 수 있다.원리 설명도 아래 와 같다.

이렇게 해서 우 리 는 기본 적 인 실현 원 리 를 분석 하고 다음 에 한 걸음 한 걸음 실현 합 시다.
비디오 재생 실현
이 글 의 주 제 는 탄막 효 과 를 실현 하 는 것 이기 때문에 생방송 의 다른 기능 과 관련 되 지 않 기 때문에 우 리 는 VideoView 를 이용 하여 로 컬 영상 을 재생 하여 최 하층 의 게임 인터페이스 를 모 의 한다.
먼저 Android Studio 를 사용 하여 DanmuTest 프로젝트 를 새로 만 든 다음 activity 를 수정 합 니 다.main.xml 의 코드 는 다음 과 같 습 니 다.

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#000">
  <VideoView
    android:id="@+id/video_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"/>
</RelativeLayout>
레이아웃 파일 의 코드 는 매우 간단 합 니 다.하나의 VideoView 만 있 습 니 다.중간 표시 로 설정 합 니 다.
그리고 MainActivity 의 코드 를 수정 합 니 다.다음 과 같 습 니 다.

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    VideoView videoView = (VideoView) findViewById(R.id.video_view);
    videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/Pixels.mp4");
    videoView.start();
  }
  @Override
  public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && Build.VERSION.SDK_INT >= 19) {
      View decorView = getWindow().getDecorView();
      decorView.setSystemUiVisibility(
          View.SYSTEM_UI_FLAG_LAYOUT_STABLE
              | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
              | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
              | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
              | View.SYSTEM_UI_FLAG_FULLSCREEN
              | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }
  }
}
위의 코드 에는 VideoView 의 가장 기본 적 인 용법 이 사용 되 었 다.onCreate()방법 에서 VideoView 의 인 스 턴 스 를 가 져 와 비디오 파일 의 주 소 를 설정 한 다음 start()방법 으로 재생 을 시작 합 니 다.물론,나 는 사전에 SD 의 루트 디 렉 터 리 에 Pixels.mp4 라 는 비디오 파일 을 준비 했다.
SD 카드 기능 을 사 용 했 지만 코드 를 간단하게 보기 위해 서 는 실행 권한 처리 에 가입 하지 않 았 습 니 다.따라서 프로젝트 의 targetSdkVersion 을 23 이하 로 지정 하 는 것 을 기억 하 세 요.
또 영상 재생 이 최고의 체험 효 과 를 낼 수 있 도록 몰입 식 패턴 의 표기 법 을 사용 했다.몰입 식 모델 에 대해 아직 이해 하지 못 하 는 친 구 는 나의 지난 글 안 드 로 이 드 상태 표시 줄 마이크로 기 교 를 참고 하여 몰입 식 모델 을 진정 으로 이해 할 수 있 습 니 다.
마지막 으로 저 희 는 AndroidManifest.xml 에서 Activity 를 가로 화면 으로 설정 하고 권한 성명 을 추가 합 니 다.다음 과 같 습 니 다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.example.guolin.danmutest">
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity" android:screenOrientation="landscape"
         android:configChanges="orientation|keyboardHidden|screenLayout|screenSize">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
  </application>
</manifest>
OK,이제 프로젝트 를 실행 할 수 있 습 니 다.프로그램 이 시작 되면 자동 으로 동 영상 을 재생 합 니 다.효 과 는 다음 그림 과 같 습 니 다.

이렇게 해서 우 리 는 첫 번 째 기능 을 실현 했다.
탄막 효 과 를 실현 하 다.
이어서 우 리 는 탄막 효 과 를 실현 하기 시작 했다.탄막 은 사용자 정의 View 로 그 위 에 주마등 과 유사 한 문자 효 과 를 나 타 낼 수 있다.시청자 들 이 발표 하 는 댓 글 은 모두 탄막 에 표시 되 지만 화면 을 빠르게 옮 겨 상호작용 을 할 수 있 을 뿐만 아니 라 영상의 정상 적 인 시청 에 도 영향 을 주지 않 는 다.
Google 은 이러한 사용자 정의 View 를 직접 작성 할 수 있 습 니 다.물론 인터넷 에서 만들어 진 오픈 소스 프로젝트 를 직접 사용 할 수도 있 습 니 다.그러면 간단하게 탄막 효 과 를 실현 하기 위해 서 저 는 삐 리 삐 리 에서 시 작 된 탄막 효과 라 이브 러 리 DanmakuFlameMaster 를 직접 사용 하려 고 합 니 다.
DanmakuFlameMaster 라 이브 러 리 의 프로젝트 홈 페이지 주 소 는:https://github.com/Bilibili/DanmakuFlameMaster
현재 Android Studio 를 사용 하여 오픈 소스 라 이브 러 리 를 도입 하 는 것 은 정말 좋 은 방법 입 니 다.build.gradle 파일 에 오픈 소스 라 이브 러 리 의존 을 추가 하면 됩 니 다.그러면 저 희 는 app/build.gradle 파일 을 수정 하고 dependencies 패키지 에 다음 과 같은 의존 도 를 추가 합 니 다.

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  compile 'com.android.support:appcompat-v7:24.2.1'
  testCompile 'junit:junit:4.12'
  compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
}
이렇게 해서 우 리 는 DanmakuFlameMaster 라 이브 러 리 를 현재 프로젝트 에 도입 했다.그리고 activity 수정main.xml 의 코드 는 다음 과 같 습 니 다.

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#000">
  <VideoView
    android:id="@+id/video_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"/>
  <master.flame.danmaku.ui.widget.DanmakuView
    android:id="@+id/danmaku_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
</RelativeLayout>
Relative Layout 에 DanmakuView 컨트롤 을 추가 한 것 을 볼 수 있 습 니 다.이 컨트롤 은 탄막 정 보 를 표시 하 는 데 사 용 됩 니 다.RelativeLayout 에 추 가 된 컨트롤 이 덮어 쓰기 때문에 DanmakuView 를 VideoView 아래 에 꼭 쓰 십시오.
다음은 MainActivity 의 코드 를 수정 합 니 다.저 희 는 여기 서 탄막 표시 논 리 를 추가 합 니 다.다음 과 같 습 니 다.

public class MainActivity extends AppCompatActivity {
  private boolean showDanmaku;
  private DanmakuView danmakuView;
  private DanmakuContext danmakuContext;
  private BaseDanmakuParser parser = new BaseDanmakuParser() {
    @Override
    protected IDanmakus parse() {
      return new Danmakus();
    }
  };
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    VideoView videoView = (VideoView) findViewById(R.id.video_view);
    videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/Pixels.mp4");
    videoView.start();
    danmakuView = (DanmakuView) findViewById(R.id.danmaku_view);
    danmakuView.enableDanmakuDrawingCache(true);
    danmakuView.setCallback(new DrawHandler.Callback() {
      @Override
      public void prepared() {
        showDanmaku = true;
        danmakuView.start();
        generateSomeDanmaku();
      }
      @Override
      public void updateTimer(DanmakuTimer timer) {
      }
      @Override
      public void danmakuShown(BaseDanmaku danmaku) {
      }
      @Override
      public void drawingFinished() {
      }
    });
    danmakuContext = DanmakuContext.create();
    danmakuView.prepare(parser, danmakuContext);
  }
  /**
   *    View       
   * @param content
   *            
   * @param withBorder
   *            
   */
  private void addDanmaku(String content, boolean withBorder) {
    BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
    danmaku.text = content;
    danmaku.padding = 5;
    danmaku.textSize = sp2px(20);
    danmaku.textColor = Color.WHITE;
    danmaku.setTime(danmakuView.getCurrentTime());
    if (withBorder) {
      danmaku.borderColor = Color.GREEN;
    }
    danmakuView.addDanmaku(danmaku);
  }
  /**
   *               
   */
  private void generateSomeDanmaku() {
    new Thread(new Runnable() {
      @Override
      public void run() {
        while(showDanmaku) {
          int time = new Random().nextInt(300);
          String content = "" + time + time;
          addDanmaku(content, false);
          try {
            Thread.sleep(time);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }).start();
  }
  /**
   * sp px   。
   */
  public int sp2px(float spValue) {
    final float fontScale = getResources().getDisplayMetrics().scaledDensity;
    return (int) (spValue * fontScale + 0.5f);
  }
  @Override
  protected void onPause() {
    super.onPause();
    if (danmakuView != null && danmakuView.isPrepared()) {
      danmakuView.pause();
    }
  }
  @Override
  protected void onResume() {
    super.onResume();
    if (danmakuView != null && danmakuView.isPrepared() && danmakuView.isPaused()) {
      danmakuView.resume();
    }
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    showDanmaku = false;
    if (danmakuView != null) {
      danmakuView.release();
      danmakuView = null;
    }
  }
  ......
}
이 를 통 해 알 수 있 듯 이 onCreate()방법 에서 우 리 는 먼저 DanmakuView 컨트롤 의 인 스 턴 스 를 얻 은 다음 에 enableDanmakuDrawingCache()방법 으로 그리 기 효율 을 높이 고 setCallback()방법 으로 리 셋 함 수 를 설정 했다.
이 어 DanmakuContext.create()방법 을 호출 하여 DanmakuContext 의 인 스 턴 스 를 만 들 었 습 니 다.DanmakuContext 는 글꼴 설정,최대 디 스 플레이 줄 수 설정 등 다양한 전역 설정 을 설정 할 수 있 습 니 다.여기 서 우 리 는 특별한 요구 가 없 기 때문에 모든 것 을 묵인 한다.
또한,전체 BaseDanmakuParser 를 직접 만 드 는 탄막 해석 기 를 만들어 야 합 니 다.
DanmakuContext 와 BaseDanmakuParser 가 있 으 면 DanmakuView 의 prepare()방법 으로 준 비 를 할 수 있 습 니 다.준비 가 완료 되면 방금 설정 한 리 셋 함수 중의 prepared()방법 을 자동 으로 호출 합 니 다.그리고 우 리 는 여기 서 DanmakuView 의 start()방법 을 호출 하면 DanmakuView 가 정상 적 인 작업 을 시작 할 수 있 습 니 다.
DanmakuView 는 이미 정상적으로 작 동 하고 있 지만 화면 에 아무런 탄막 정보 가 없 으 면 효과 가 보이 지 않 기 때문에 탄막 메 시 지 를 추가 하 는 기능 도 추가 해 야 합 니 다.
addDanmaku()방법 을 관찰 합 니 다.이 방법 은 Danmaku View 에 탄막 메 시 지 를 추가 하 는 데 사 용 됩 니 다.그 중에서 먼저 createDanmaku()방법 을 호출 하여 BaseDanmaku 인 스 턴 스 를 만 들 었 습 니 다.TYPESCROLL_RL 은 이것 이 오른쪽 에서 왼쪽으로 구 르 는 탄막 이 라 고 표시 한 다음 에 우 리 는 탄막 의 내용,글꼴 크기,색상,표시 시간 등 각종 세부 사항 을 설정 할 수 있다.addDanmaku()방법 에 with Border 인자 가 있 습 니 다.이 인 자 는 탄막 메시지 에 테두리 가 있 는 지 여 부 를 지정 하 는 데 사 용 됩 니 다.그래 야 자신 이 보 낸 탄막 과 다른 사람 이 보 낸 탄막 을 구분 할 수 있 습 니 다.
이렇게 하면 우 리 는 가장 기본 적 인 탄막 기능 을 완성 할 수 있 습 니 다.이 제 는 다른 사람 이 보 낸 탄막 메 시 지 를 받 을 때 addDanmaku()방법 으로 이 탄막 을 Danmaku View 에 추가 하면 됩 니 다.그러나 다른 사람 이 보 낸 소식 을 받 는 것 은 인 스 턴 트 메 신 저 기술 과 관련 되 었 다.분명히 이 글 에서 복잡 한 인 스 턴 트 메 신 저 기술 도 설명 할 수 없 을 것 이다.그래서 여기 서 나 는 generateSomeDanmaku()방법 을 써 서 랜 덤 으로 탄막 메 시 지 를 만 들 었 다.그러면 물고기 싸움 과 유사 한 탄막 효 과 를 모 의 할 수 있다.
그 밖 에 우 리 는 onPause(),onResume(),onDestroy()방법 에서 DanmakuView 의 자원 이 방출 될 수 있 도록 논리 적 처 리 를 해 야 한다.
다음 그림 과 같이 프로그램 을 다시 실행 합 니 다.

이렇게 해서 우 리 는 두 번 째 단계 의 기능 도 실현 했다.
조작 인터페이스 가입
그러면 세 번 째 기능 을 실현 하고 탄막 메 시 지 를 보 내 는 조작 인터페이스 에 가입 하 겠 습 니 다.
우선 activity 수정main.xml 의 코드 는 다음 과 같 습 니 다.

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="#000">
  ......
  <LinearLayout
    android:id="@+id/operation_layout"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_alignParentBottom="true"
    android:background="#fff"
    android:visibility="gone">
    <EditText
      android:id="@+id/edit_text"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"
      />
    <Button
      android:id="@+id/send"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:text="Send" />
  </LinearLayout>
</RelativeLayout>
여기 서 우 리 는 조작 인터페이스 로 Linear Layout 를 추가 한 것 을 볼 수 있다.LinearLayout 에는 복잡 한 컨트롤 이 없습니다.내용 을 입력 하 는 데 사용 되 는 EditText 만 있 고,단추 하 나 는 탄막 을 보 내 는 데 사 용 됩 니 다.처음에 LinearLayout 를 숨 겼 습 니 다.이 조작 인터페이스 가 VideoView 를 계속 가 릴 수 없 기 때문에 사용자 가 탄막 을 보 내 려 고 할 때 만 표시 해 야 합 니 다.
다음은 MainActivity 의 코드 를 수정 하고 이 안에 탄막 을 보 내 는 논 리 를 추가 합 니 다.다음 과 같 습 니 다.

public class MainActivity extends AppCompatActivity {
  ......
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ......
    final LinearLayout operationLayout = (LinearLayout) findViewById(R.id.operation_layout);
    final Button send = (Button) findViewById(R.id.send);
    final EditText editText = (EditText) findViewById(R.id.edit_text);
    danmakuView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        if (operationLayout.getVisibility() == View.GONE) {
          operationLayout.setVisibility(View.VISIBLE);
        } else {
          operationLayout.setVisibility(View.GONE);
        }
      }
    });
    send.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View view) {
        String content = editText.getText().toString();
        if (!TextUtils.isEmpty(content)) {
          addDanmaku(content, true);
          editText.setText("");
        }
      }
    });
    getWindow().getDecorView().setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() {
      @Override
      public void onSystemUiVisibilityChange(int visibility) {
        if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) {
          onWindowFocusChanged(true);
        }
      }
    });
  }
  ......
}
이곳 의 논 리 는 비교적 간단 합 니 다.우 리 는 먼저 DanmakuView 에 클릭 이 벤트 를 설정 하고 화면 을 클릭 하면 이 클릭 이 벤트 를 촉발 합 니 다.그 다음 에 판단 을 한다.만약 에 조작 인터페이스 가 숨겨 져 있 으 면 이 를 나타 내 고 조작 인터페이스 가 표시 되 어 있 으 면 이 를 숨 기 면 화면 을 클릭 하여 조작 인터페이스의 숨 김 과 표 시 를 간단하게 실현 할 수 있다.
그 다음 에 우 리 는 보 내기 단 추 를 누 르 면 이 벤트 를 등록 하고 보 낼 때 EditText 의 입력 내용 을 가 져 온 다음 에 addDanmaku()방법 으로 이 메 시 지 를 Danmaku View 에 추가 합 니 다.또한,이 탄막 은 우리 가 직접 보 낸 것 이기 때문에 addDanmaku()방법의 두 번 째 매개 변 수 는 true 에 전달 해 야 합 니 다.
마지막 으로 시스템 입력 법 이 팝 업 될 때 초점 을 잃 어 버 려 몰입 식 모드 를 종료 하기 때문에 시스템 전역 의 UI 변 화 를 감청 하여 프로그램 이 몰입 식 모드 에 있 을 수 있 도록 한다.
이렇게 하면 우 리 는 모든 코드 를 완성 할 수 있 으 니,지금 은 최종 효 과 를 실행 해 볼 수 있다.영화 가 방영 되 는 동시에 GIF 캡 처 를 해서 생 성 된 파일 이 너무 커서 업로드 할 수 없 기 때문에 여 기 는 제 가 영화 가 멈 춘 상태 에서 작 동 했 습 니 다.효 과 는 다음 그림 과 같다.

우리 가 보 낸 탄막 은 녹색 테두리 로 둘러싸 여 있어 다른 탄막 과 쉽게 분리 되 는 것 을 볼 수 있다.
이렇게 해서 우 리 는 세 번 째 단계 의 기능 도 실현 했다.
비록 지금 우 리 는 매우 좋 은 탄막 효 과 를 성공 적 으로 실현 하 였 지만,사실은 이것 은 단지 DanmakuFlameMaster 창고 가 제공 하 는 가장 기본 적 인 기능 일 뿐이다.삐 리 삐 리 가 제공 하 는 이 탄막 오픈 소스 라 이브 러 리 는 매우 풍부 한 기능 을 가지 고 있 으 며,각종 탄막 스타일,특수 효과 등 을 포함한다.그러나 이 글 의 주요 목 표 는 탄막 효과 가 실현 되 는 방향 을 알 리 는 것 이지 DanmakuFlameMaster 라 이브 러 리 에 대해 전면적 인 분석 을 하 는 것 이 아니다.이 라 이브 러 리 에 관심 이 많다 면 github 홈 페이지 에서 더 많은 용법 을 배 울 수 있 습 니 다.
위 에서 말 한 것 은 편집장 님 께 서 소개 해 주신 안 드 로 이 드 버들붕어 생방송 의 탄막 효과 입 니 다.도움 이 되 셨 으 면 좋 겠 습 니 다.궁금 한 점 이 있 으 시 면 메 시 지 를 남 겨 주세요.편집장 님 께 서 바로 답 해 드 리 겠 습 니 다.여기 서도 저희 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기