Cool Android interactive animation and visual effects: High imitation of the Yinyuetai playback page

Cool Android interactive animation and visual effects: High imitation of the Yinyuetai playback page

The new version of the Yin Yue Tai APP has a very interesting interactive playback page. You can drag the player down, and the page will become transparent and gradient. Then you can drag it left or right to close the player at the bottom. Then click on the video list and a page will pop up. It's very cool, so I implemented this interactive and cool player page by myself.

[[181643]]

1. Without further ado, let’s demonstrate the effect

1.1. Click on a video, then drag your finger up and down, the player will change its size ratio gradually, and the video-related information will change its transparency gradually

1.2. The player can only be dragged left and right when it is at the bottom. At this time, the player has a gradual transparency change. Dragging within a certain range can close the player; then it can only be dragged upward within a short distance of the original position.

1.3. When you click on the video list, if the last video was closed by dragging left or right, there will be a pop-up play page effect; if it is the back key or the back arrow, there will be no effect

2. Explanation of the implementation ideas

Undoubtedly, you need to customize a container, handle its touch events, and handle its child Views differently. Touch event handling is best done with ViewDragHelper, and then you need to implement the container onMeasure and onLayout. Due to the use of ViewDragHelper, some pitfalls will be explained during code analysis.

The playback page is a question of whether to use a new Activity or just the View of the current Activity. Since the user can slide the video list when the player is reduced to the bottom, I personally think that you can just place a custom container in the current Activity. Therefore, for efficiency considerations, you can use ViewStub for lazy loading. For the convenience of demonstration here, I will directly use the View format.

3. Code analysis

3.1. Required variables

3.2. Initialize ViewDragHelper, then post to get two child Views. It is mandatory to have only two child elements here.

3.3. ViewDragHelper callback needs to do a lot of things. When mFlexView is dragged, the corresponding change effects of mFlexView and mFollowView need to be set at the same time. When mFlexView is released, the closing or folding effects need to be processed.

3.4. Next is to deal with measurement and positioning. The arrangement effect we achieved is similar to the vertical arrangement effect of LinearLayout. Here, the heightUse of measureChildWithMargins is a problem. When onLayout is called and the position cache is not empty, it is directly positioned because ViewDragHelper is processing touch events and the child elements are doing some translation. If an element updates the UI, it will cause a re-layout. For example, my player will do this when updating the time TextView. Therefore, the position is recorded in the onViewPositionChanged method of FlexCallback, and the position can be restored when re-layout. This has also been a pit for a long time.

3.5. Touch event processing. Since scaling will not affect the real width and height of mFlexView, ViewDragHelper will still block the real width and height of mFlexView. Therefore, it is necessary to determine whether the finger falls within the visual range of mFlexView before calling the shouldInterceptTouchEvent method of ViewDragHelper.

3.6. In computeScroll, if mIsClosing is true, the entire translation is closed and the callback event is notified

3.7. The container is implemented. Next, we inherit YytLayout to implement the combined control of the player page, and then encapsulate some common methods. Here we use the famous Ijkplayer to implement the player, shielding the touch events of IjkVideoView and handling them by ourselves; by the way, in order to achieve the effect of the player Controller following the drag and zoom, the commonly used PopupWindow implementation idea is abandoned. IjkController is directly added to IjkVideoView, otherwise it is too troublesome to implement the pop-up window following the player

  1. /**
  2. * Created by Oubowu on 2016/12/27 17:32.<p>
  3. * Specific implementation of the playback page of the imitation music platform, in the form of combined controls
  4. */
  5. public class YytPlayer extends YytLayout {
  6. private IjkController mIjkController;
  7. private IjkVideoView mIjkVideoView;
  8. private ImageView mIvAvatar;
  9. private TextView mTvName;
  10. private TextView mTvTime;
  11. private TextView mTvTitle;
  12. private TextView mTvDesc;
  13. private RecyclerView mYytRecyclerView;
  14. private VideoListAdapter mVideoListAdapter;
  15. public YytPlayer(Context context, AttributeSet attrs) {
  16. super(context, attrs);
  17. init(context, attrs);
  18. }
  19. private void init(Context context, AttributeSet attrs) {
  20. // Inherit YytLayout and reduce the level through the merge tag to realize the combination of controls
  21. LayoutInflater. from (context).inflate(R.layout.yyt_player, this, true );
  22. setOnLayoutStateListener(new OnLayoutStateListener() {
  23. @Override
  24. public void onClose() {
  25. setVisibility( View .INVISIBLE);
  26. mIjkVideoView.release( true );
  27. }
  28. });
  29. mIjkVideoView = (IjkVideoView) findViewById(R.id.ijk_player_view);
  30. final int scaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
  31. mIjkVideoView.setOnTouchListener(new OnTouchListener() {
  32. float mDownX = 0;
  33. float mDownY = 0;
  34. boolean mClickCancel;
  35. @Override
  36. public boolean onTouch( View v, MotionEvent event) {
  37. float x = event.getX();
  38. float y = event.getY();
  39. switch (event.getAction()) {
  40. case MotionEvent.ACTION_DOWN:
  41. mDownX = x;
  42. mDownY = y;
  43. break;
  44. case MotionEvent.ACTION_MOVE:
  45. if (Math. abs (mDownX - x) > scaledTouchSlop || Math. abs (mDownY - y) > scaledTouchSlop) {
  46. mClickCancel = true ;
  47. }
  48. break;
  49. case MotionEvent.ACTION_UP:
  50. if (!mClickCancel && Math. abs (mDownX - x) <= scaledTouchSlop && Math. abs (mDownY - y) <= scaledTouchSlop) {
  51. // Click events occasionally fail, so I have to solve it myself here
  52. if (isHorizontalDragEnable()) {
  53. expand();
  54. } else {
  55. mIjkVideoView.toggleMediaControlsVisibility();
  56. }
  57. }
  58. mClickCancel = false ;
  59. break;
  60. case MotionEvent.ACTION_CANCEL:
  61. mClickCancel = false ;
  62. break;
  63. }
  64. return   true ;
  65. }
  66. });
  67. mIvAvatar = (ImageView) findViewById(R.id.iv_avatar);
  68. mTvName = (TextView) findViewById(R.id.tv_name);
  69. mTvTime = (TextView) findViewById(R.id.tv_time);
  70. mTvTitle = (TextView) findViewById(R.id.tv_title);
  71. mTvDesc = (TextView) findViewById(R.id.tv_desc);
  72. mVideoListAdapter = new VideoListAdapter();
  73. mVideoListAdapter.setOnItemClickCallback(new OnItemClickCallback() {
  74. @Override
  75. public void onClick( View   view , int position) {
  76. int pos = ( Integer ) view .getTag();
  77. VideoSummary summary = mVideoListAdapter.getData().get(pos);
  78. playVideo(mVideoListAdapter.getData(), summary);
  79. }
  80. });
  81. mYytRecyclerView = (RecyclerView) findViewById(R.id.yyt_recycler_view);
  82. GridLayoutManager gridLayoutManager = new GridLayoutManager(context, 2, LinearLayoutManager.VERTICAL, false );
  83. mYytRecyclerView.setLayoutManager(gridLayoutManager);
  84. mYytRecyclerView.setNestedScrollingEnabled( false );
  85. mYytRecyclerView.addItemDecoration(new VideoListItemDecoration(context));
  86. mYytRecyclerView.setAdapter(mVideoListAdapter);
  87. }
  88. // Play the video
  89. private void playVideo(String path, String name ) {
  90. try {
  91. if (mIjkController == null ) {
  92. IjkMediaPlayer.loadLibrariesOnce( null );
  93. IjkMediaPlayer.native_profileBegin( "libijkplayer.so" );
  94. mIjkController = new IjkController(mIjkVideoView, name );
  95. mIjkController.setOnViewStateListener(new IjkController.OnViewStateListener() {
  96. @Override
  97. public void onBackPress() {
  98. stop();
  99. }
  100. });
  101. mIjkVideoView.setMediaController(mIjkController);
  102. mIjkVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
  103. @Override
  104. public void onPrepared(IMediaPlayer mp) {
  105. mIjkVideoView.start();
  106. }
  107. });
  108. mIjkVideoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
  109. @Override
  110. public boolean onError(IMediaPlayer mp, int what, int extra) {
  111. Toast.makeText(getContext(), "Video playback error ╮(╯Д╰)╭" , Toast.LENGTH_SHORT).show();
  112. return   true ;
  113. }
  114. });
  115. } else {
  116. // Reset the video name
  117. mIjkController.setVideoName( name );
  118. }
  119. // Set the TextureView player to scale normally
  120. mIjkVideoView.setRender(IjkVideoView.RENDER_TEXTURE_VIEW);
  121. // Because each setRender will remove the view and add it again, in order to achieve the zoom effect, the controller is added to IjkVideoView, so it must be added again here to be on the top of IjkVideoView
  122. mIjkController.updateControlView();
  123. // Display the loading bar
  124. mIjkController.showProgress();
  125. // Play the video
  126. mIjkVideoView.setVideoURI(Uri.parse(path));
  127. } catch (UnsatisfiedLinkError e) {
  128. e.printStackTrace();
  129. Toast.makeText(getContext(), "Your CPU is" + Build.CPU_ABI + ", the compiled version used by the current player" + BuildConfig.FLAVOR + "Does not match!" , Toast.LENGTH_LONG).show();
  130. }
  131. }
  132. /**
  133. * Display the layout and play the video
  134. *
  135. * @param data video list, used for the list layout below the playback page
  136. * @param summary Playing video information
  137. */
  138. public void playVideo(List<VideoSummary> data, VideoSummary summary) {
  139. // Get the data and set the relevant information to the playback layout
  140. Glide. with (getContext()). load (summary.mTopicImg).transform(new GlideCircleTransform(getContext())). into (mIvAvatar);
  141. mTvName.setText(summary.mTopicName);
  142. mTvTime.setText(summary.mPtime);
  143. mTvTitle.setText(Html.fromHtml(summary.mTitle));
  144. if (summary.mDescription.isEmpty()) {
  145. mTvDesc.setText(summary.mTopicDesc);
  146. } else {
  147. mTvDesc.setText(Html.fromHtml(summary.mDescription));
  148. }
  149. // Set YytLayout to be visible and expand
  150. setVisibility( View .VISIBLE);
  151. expand();
  152. mVideoListAdapter.setData(data);
  153. mVideoListAdapter.setItemWidth(mYytRecyclerView.getWidth() / 2);
  154. mVideoListAdapter.notifyDataSetChanged();
  155. // Play the video
  156. playVideo(summary.mMp4HdUrl == null ? summary.mMp4Url : summary.mMp4HdUrl, summary.mTitle);
  157. }
  158. // Start playing
  159. public void start() {
  160. if (mIjkVideoView != null && !mIjkVideoView.isPlaying()) {
  161. mIjkVideoView.start();
  162. }
  163. }
  164. // Pause playback
  165. public void pause() {
  166. if (mIjkVideoView != null && mIjkVideoView.isPlaying()) {
  167. mIjkVideoView.pause();
  168. }
  169. }
  170. // Stop playing
  171. public void stop() {
  172. setVisibility( View .INVISIBLE);
  173. if (mIjkVideoView != null ) {
  174. mIjkVideoView.release( true );
  175. }
  176. }
  177. public boolean isShowing() {
  178. return getVisibility() == VISIBLE;
  179. }
  180. }

4. Summary

It's not difficult, but it requires imagination to pay attention to the details. You may want to use your imagination when you see the interesting interactions. Next, you may want to realize the effect of UC browser player, which is also very interesting.

<<:  Deep understanding of Swift dispatch mechanism

>>:  9 Best JavaScript Mobile App Development Frameworks

Recommend

How does Tongcheng operate blind boxes that are popular all over the Internet?

Tongcheng Travel spent only 9 yuan to buy the rea...

Surprise! Windows 10 loves to spread your WiFi password

Windows 10's Wi-FiSense feature has a securit...

Facebook pays tribute to Steve Jobs on his 60th birthday

I don't want to go to work because I'm la...

Tencent's love-style product operation method

What does it feel like to be in love? Most of the...

6 tips for community operation!

A new community is made up of many different mand...

Let’s talk about how to cleverly deal with iOS keyboard problems?

Preface Students who have written mobile applicat...