Android开源 - 高仿全民 TV

JameyGuenth 7年前
   <p>直播一直是个很火的趋势,特别是在今年以来让不少平凡的草根百姓也和狠狠“火”了一把, 什么某游戏主播年薪一千五百万,某女主播直播吃饼干,吃饭,月入十万 ,等等。。但这些跟我们没有半点关系。但都没有关系,代码还能愉快的敲下去,砖还得搬,搬还得钻。也是抱着学习的态度和对技术的热诚,动动手写下这项目,然后分享给大家。先看下今天的Agenda:</p>    <ol>     <li> <p>树形结构的架构思路</p> </li>     <li> <p>模块化的开放方法</p> </li>     <li> <p>总结</p> </li>    </ol>    <h3><strong>1.树形结构的架构思路</strong></h3>    <p>很多有经验的开发人员,在一定的项目洗礼之后,逐渐形成一套可行的敏捷开发的方案,就是俗话中的套路。本人也有一套路,命称树形套路 。在这里阐述的所谓树形,是从项目出发,以数据走向到每个功能点、页面的发散型项目观,在树形中的每个节点,可以按照个人的特点以为一个Activity、Fragment等作为一个节点,Intent、FragmentAdapter作为连接节点之间的纽带。 再细化,将Activity、Fragment作为一个树根发散来看待项目架构。最终,在树形项目架构中,便会产生1级、2级、3级节点出来 。 在同一级别的节点,有共同点,使用模板设计模式从中抽取。 </p>    <p>以本项目中的推荐页来说</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ddf93826f6d4c869d3812afbc3b4a8a1.jpg"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3c54ba1e0388906718989bc3ef4ffc9c.jpg"></p>    <p>从功能层面看到 tab 精彩推荐和 tab 颜值控、英雄联盟等属于第一节点级别。但是精彩推荐和其他tab下的效果有很大的差别。 而其他tab下的都是列表形式。那么精彩推荐可以单独分离一个Fragment,其他Tab可以抽象出一个BaseLiveWraperFragment。在这些BaseLiveWraperFragment中,其中颜值控的页面每个Item显示和其他不同。那么以LoveLiveListFragment作为父类,将数据绑定和Item布局的设定延迟到具体的子类LoveLiveListFragment中,实现子列表的独特展现。示例代码:</p>    <pre>  <code class="language-java">public class LoveLiveListFragment extends BaseLiveWraperFragment {        private LiveInteractor mLiveInteractor;      private int mScreenWidth;        public static LoveLiveListFragment newInstance(Bundle args) {          LoveLiveListFragment fragment = new LoveLiveListFragment();          fragment.setArguments(args);          return fragment;      }            @Override      public int getListItemLayout() {          return R.layout.listitem_love; //item 布局      }            @Override      protected void convertItem(ViewHolder holder, final PlayBean playBean, int position) { //item数据绑定          holder.setImageUrl(R.id.thumnails,playBean.thumb,new GlideRoundTransform(mActivity,5));          holder.setText(R.id.tv_viewnum,playBean.view);          holder.setText(R.id.intro,playBean.title);          ..省略数行.      }        @Override      public void getDataError(String errmsg) {          showToast("获取颜值控数据失败");      }  }    </code></pre>    <p>父类BaseLiveWraperFragment相关方法:</p>    <pre>  <code class="language-java">public class BaseLiveWraperFragment extends BaseListFragment                   {      protected String mUrl;      private LiveInteractor mLiveInteractor;      private String mTag;            public static BaseLiveWraperFragment newInstance(Bundle args) {          BaseLiveWraperFragment fragment = new BaseLiveWraperFragment();          fragment.setArguments(args);          return fragment;      }            public int getListItemLayout() { //item 布局          return R.layout.listitem_live;      }        @Override      protected void convertItem(ViewHolder holder, final PlayBean playBean, int position) { //item数据绑定          holder.setImageUrl(R.id.thumnails,playBean.thumb,new GlideRoundTransform(mActivity,5));          holder.setText(R.id.title,playBean.title);          ..省略数行.      }                  @Override      protected void initData() {  //获取网络数据          Bundle arguments = getArguments();          mUrl = arguments.getString("url", "");          mTag = arguments.getString("tag", "");          mLiveInteractor = new LiveInteractor();          mLiveInteractor.loadPlayList(this,mUrl);      }      @Override      public void getDataError(String errmsg) {          showToast("获取"+mTag+"数据失败");      }  }          </code></pre>    <p>而以BaseListFragment为跟节点,很多列表的实现都以此延伸,从然产生另一个“树”。这样,咱们可以将大大提高了代码了重用性、拓展性。结构也清晰明了。 对这一块有相同的开发思路就是对Presenter的处理是类似的,这里不深探究。</p>    <h3><strong>2. 模块化的开放方法</strong></h3>    <p>如果说树形方法是纵向的开发思路。那么模块化的开发方法就是横向的实现方式。在每一个节点中,以Activity为例,一个Activity所包含的功能中,可以想象为若干的Part组成。Part可以是功能或者View都可以。以项目中的直播页面为示例: </p>    <p>就目前的CommonLiveUI中:本人将Part分为:</p>    <ol>     <li> <p>播放器</p> </li>     <li> <p>竖屏控件持有者</p> </li>     <li> <p>横屏控件持有者</p> </li>    </ol>    <p>在onCreate方法中对其初始化操作,完了就放养了</p>    <pre>  <code class="language-java">@Override  protected void onCreate(Bundle savedInstanceState) {             getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);            super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);      super.onCreate(savedInstanceState);      setContentView(R.layout.act_commonliveplayer);      //1.初始化dagger2      mActivityComponent.inject(this);      mBasePresent=livePlayerPresenterImpl;      livePlayerPresenterImpl.attachView(this);        initPlayer();  //2.初始化播放器      initVerControll();//3.初始化竖屏控件持有者      initHorContrll();//4.初始化横屏控件持有者      initData();//5.初始化数据  }  </code></pre>    <p>初始化完,让各回各家,各找各妈。结合mvp+dagger2下来,优雅的解耦。而其余的代码,就是各种回调,刷新UI等等。</p>    <pre>  <code class="language-java">protected void onResume() {          super.onResume();          if (playerHolder!=null)          playerHolder.onResume();      }      @Override      protected void onPause() {          super.onPause();          if (playerHolder!=null)          playerHolder.onPause();      }        @Override      protected void onDestroy() {          if (playerHolder!=null){              playerHolder.release();              playerHolder=null;          }          verticalControll.onDestroy();          horizontalControll.onDestroy();          super.onDestroy();            }        @Override      protected void toPrepare() {          if (playerHolder!=null)          playerHolder.prepare();      }        @Override      public void onConfigurationChanged(Configuration newConfig) {          super.onConfigurationChanged(newConfig);          if (getRequestedOrientation()==ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){              //portrait          }else {              //landscape          }      }      @Override      public void setRequestedOrientation(int requestedOrientation) {          super.setRequestedOrientation(requestedOrientation);          if (requestedOrientation==ActivityInfo.SCREEN_ORIENTATION_PORTRAIT){              isVertical=true;              verticalControll.onCreate();              horizontalControll.onDestroy();          }else {              isVertical=false;              horizontalControll.onCreate();              verticalControll.onDestroy();          }      }            @Override      public void onConnecting() {          mLoadingView.setVisibility(View.VISIBLE);      }            @Override      public void onReConnecting() {          showToastTips("正在重连...");      }        @Override      public void onConnectSucces() {          mLoadingView.setVisibility(View.GONE);      }            @Override      public void onConnectFailed() {          showToastTips("连接失败");      }        @Override      public void onPlayComleted() {          showToastTips("主播离开了");      }        @Override      public void onPlayerStart() {          bgImage.animate().alpha(0).setDuration(1000).start();      }        @Override      public void onPlayePause() {      }        @Override      public void onRoomData(JSONObject roomJson) {          mRoomDataController = new RoomDataController(roomJson);          mPlayerPath = mRoomDataController.getPlayerPath(0);          playerHolder = new LivePlayerHolder(this,mSurfaceView,mCodec,mPlayerPath);          playerHolder.startPlayer();      }        @Override      public void onBackPressed() {          if (getRequestedOrientation()==ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE){              setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);              ViewGroup.LayoutParams params =                      (ViewGroup.LayoutParams) mSurfaceView.getLayoutParams();              params.width=mPortWidth;              params.height=mPortHeight;                    ViewGroup.LayoutParams mStatusbarParams = mStatusbar.getLayoutParams();              mStatusbarParams.height= (int) (getResources().getDimension(R.dimen.status_bar_height)+0.5f);              getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);              return;          }          if (playerHolder!=null)              playerHolder.release();          super.onBackPressed();      }        @Override      public void onVerticalClickFullScreen() {          setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);          Display display =                  getWindow().getWindowManager().getDefaultDisplay();          DisplayMetrics metrics = new DisplayMetrics();          display.getMetrics(metrics);                int heightPixels = metrics.heightPixels;          int widthPixels = metrics.widthPixels;          Log.e("metrics","heightPixels"+heightPixels);          Log.e("metrics","widthPixels"+widthPixels);          ViewGroup.LayoutParams params =                  (ViewGroup.LayoutParams) mSurfaceView.getLayoutParams();                int height = params.height;          int width = params.width;                //status bar          ViewGroup.LayoutParams mStatusbarParams = mStatusbar.getLayoutParams();          mStatusbarParams.height=0;          getWindow().getDecorView().setSystemUiVisibility(View.INVISIBLE);                Log.e("mSurfaceView","width"+width);          Log.e("mSurfaceView","height"+height);                mPortWidth=width;          mPortHeight=height;          params.width=widthPixels;          params.height=heightPixels;      }        @Override      public boolean onTouch(View v, MotionEvent event) {          LogUtil.i("TOUCH  "+isVertical);          verticalControll.onTouchEvent(isVertical,event);          horizontalControll.onTouchEvent(isVertical,event);          return false;      }  }  </code></pre>    <p>代码不超过300行。以此完成功能的模块化,非常直观的阅读。这样,少挖点坑,为后来接手的人减少点酷刑。 </p>    <p>不过值得一提的是,在各种“持有者” 中,最好类似生命周期onCreate()和onDestory()方法,方便做资源释放,减少内存泄漏的发生。</p>    <p>部分截图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e694c93ed4e990f76098caf6fb806a3d.jpg"><br> <img src="https://simg.open-open.com/show/3f8fe9aa3442fbdd3bad9d0f0c03b132.jpg"></p>    <p>横屏:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ac0a4d02f09096668cdbf755c2e9c1c4.jpg"></p>    <h3><strong>总结</strong></h3>    <p>临时受邀于何俊林 ,写一篇分享文,也是非常感谢何俊林愿意我在此给大家分享。</p>    <p> </p>    <p> </p>    <p> </p>