Android開源 - 高仿全民 TV

直播一直是個很火的趨勢,特別是在今年以來讓不少平凡的草根百姓也和狠狠“火”了一把, 什么某游戲主播年薪一千五百萬,某女主播直播吃餅干,吃飯,月入十萬 ,等等。。但這些跟我們沒有半點關系。但都沒有關系,代碼還能愉快的敲下去,磚還得搬,搬還得鉆。也是抱著學習的態度和對技術的熱誠,動動手寫下這項目,然后分享給大家。先看下今天的Agenda:

  1. 樹形結構的架構思路

  2. 模塊化的開放方法

  3. 總結

1.樹形結構的架構思路

很多有經驗的開發人員,在一定的項目洗禮之后,逐漸形成一套可行的敏捷開發的方案,就是俗話中的套路。本人也有一套路,命稱樹形套路 。在這里闡述的所謂樹形,是從項目出發,以數據走向到每個功能點、頁面的發散型項目觀,在樹形中的每個節點,可以按照個人的特點以為一個Activity、Fragment等作為一個節點,Intent、FragmentAdapter作為連接節點之間的紐帶。 再細化,將Activity、Fragment作為一個樹根發散來看待項目架構。最終,在樹形項目架構中,便會產生1級、2級、3級節點出來 。 在同一級別的節點,有共同點,使用模板設計模式從中抽取。 

以本項目中的推薦頁來說

從功能層面看到 tab 精彩推薦和 tab 顏值控、英雄聯盟等屬于第一節點級別。但是精彩推薦和其他tab下的效果有很大的差別。 而其他tab下的都是列表形式。那么精彩推薦可以單獨分離一個Fragment,其他Tab可以抽象出一個BaseLiveWraperFragment。在這些BaseLiveWraperFragment中,其中顏值控的頁面每個Item顯示和其他不同。那么以LoveLiveListFragment作為父類,將數據綁定和Item布局的設定延遲到具體的子類LoveLiveListFragment中,實現子列表的獨特展現。示例代碼:

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("獲取顏值控數據失敗");
    }
}

父類BaseLiveWraperFragment相關方法:

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+"數據失敗");
    }
}


  

而以BaseListFragment為跟節點,很多列表的實現都以此延伸,從然產生另一個“樹”。這樣,咱們可以將大大提高了代碼了重用性、拓展性。結構也清晰明了。 對這一塊有相同的開發思路就是對Presenter的處理是類似的,這里不深探究。

2. 模塊化的開放方法

如果說樹形方法是縱向的開發思路。那么模塊化的開發方法就是橫向的實現方式。在每一個節點中,以Activity為例,一個Activity所包含的功能中,可以想象為若干的Part組成。Part可以是功能或者View都可以。以項目中的直播頁面為示例: 

就目前的CommonLiveUI中:本人將Part分為:

  1. 播放器

  2. 豎屏控件持有者

  3. 橫屏控件持有者

在onCreate方法中對其初始化操作,完了就放養了

@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.初始化數據
}

初始化完,讓各回各家,各找各媽。結合mvp+dagger2下來,優雅的解耦。而其余的代碼,就是各種回調,刷新UI等等。

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;
    }
}

代碼不超過300行。以此完成功能的模塊化,非常直觀的閱讀。這樣,少挖點坑,為后來接手的人減少點酷刑。 

不過值得一提的是,在各種“持有者” 中,最好類似生命周期onCreate()和onDestory()方法,方便做資源釋放,減少內存泄漏的發生。

部分截圖:


橫屏:

總結

臨時受邀于何俊林 ,寫一篇分享文,也是非常感謝何俊林愿意我在此給大家分享。

 

 

 

 本文由用戶 JameyGuenth 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!