公司(视频 社交)项目分享

最后更新于:2022-04-01 14:19:53

最近公司工作比较轻松,就把以前的项目 拿来整理下。以前公司做视频社交这一块,类似于YY直播。 ### 展示 ##### **先来个动态图** ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5d8d6f5.jpg "") ##### **再简单看一下主要界面** 首页第一个界面,这里可以看美女,看直播 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5de32ec.jpg "") 这是任务列界面,可以领取每日任务,任务分成长 和推荐 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5e1907b.jpg "") 好友列表 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5e35fb7.jpg "") 用户的个人中心页 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5e53962.jpg "") 直播间 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5e76ea7.jpg "") 用户的个人中心页 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5e9cb05.jpg "") 充值方式页 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5ebdecd.jpg "") 银联支付页 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5ed5d72.jpg "") ### 项目组成 这个项目主要的重点就两个地方,看视频,和 与主播互动。 整个视频流是用RTMP协议的,文字聊天走的是openfire+asmack . PS:整个项目,我们产品设计的很不错的,只是我水平有限,有些功能 实现不了! 再介绍下,整个项目都用到了什么? 程序框架:SlidingMenu+Viewpager+fragment 请求服务器: asynchttpclient 解析数据:Gson 消息推送: Jpush 页面数据分析: Umeng 充值方式: alipay +银联+yeepay+短信充值 图片缓存: afinal 自定义view: Pulltoresfresh+拼音排序联系人+horizontallistview+verticalviewpager… ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5f09528.jpg "") 另,我把公司的项目写出来,是因为公司的服务器停掉了,所以,一些数据,是我自己抓出其他应用里面的(抓取数据的方法,在上面一篇文章里)…. 首先就是整个项目最下面是mainactivity,这个大家是都有共识的,在mainactivity 上面 我们就要 引入slidingmenu, 关于slidingmenu的下载,就不介绍了,这里直接拿来用 包结构比较清晰 ![这里写图片描述](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-04-06_5704ac5f72481.jpg "") , 整个项目首先,由splashactivity 欢迎界面 ,进行检查,然后跳转到mainactivity,mainactivity 中包含slidingmenu,slidingmenu的中间界面 添加了viewpager,viewpager 里面添加了四个fragment。 由于默认v4包里的viewpager 会默认混存数据,即使你设置了setOffscreenPageLimit(0),所以这里替换掉原生的v4包,并且通过fragment 的 setuservisibilityhint 方法来,控制 fragment 界面的动态刷新。 主界面的第一个fragment ,也就是约美女的界面,通过fragmenttabhost,来实现约美女,和看直播的切换。 在约美女中,就是一个简单地pulltorefreshlistview,适配了一个item.点击item 进入用户的个人中心, 个人中心 顶部 是个人的宣传适配,下面,是用户自己上传的 公开专辑,或者私密专辑。可以点赞,关注他。 做任务界面是有两种任务,一种是 每日任务,就是 登陆,签到,另一种是下载app 得积分任务。 下载app 可以控制 下载 暂停。 在我的关注界面。是你关注的好友,这个跟微信的联系人控件是一样的。首字母排序,用到了比较器。 首先 把汉字,对应首字母提取出来,然后与A-Z 排序。以及特殊字符~。 ### 基本代码 整个项目 基本框架 简单描述下 在MainActiviy中初始化 slidingmenu。MainActivity 布局文件 `<?xml version="1.0" encoding="utf-8"?> <com.os.slidingmenu.SlidingMenu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sliding="http://schemas.android.com/apk/res-auto" android:id="@+id/slidingmenu" android:layout_width="fill_parent" android:layout_height="fill_parent" sliding:behindOffset="60dp" sliding:fadeEnabled="true" sliding:mode="left" sliding:secondaryShadowDrawable="@drawable/sliding_shadow_right" sliding:shadowDrawable="@drawable/sliding_shadow_left" sliding:shadowWidth="10dp" sliding:touchModeAbove="fullscreen" sliding:touchModeBehind="margin" /> ` MainActivity这个类进行初始化。 ~~~ package com.os.activity; import java.lang.ref.WeakReference; import com.os.activity.base.BaseFragmentActivity; import com.os.activity.base.BaseSlidingFragment; import com.os.activity.sliding.LeftFragment; import com.os.activity.sliding.RightFragment; import com.os.slidingmenu.R; import com.os.slidingmenu.SlidingMenu; import com.os.ui.MainHallFragment; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; public class MainActivity extends BaseFragmentActivity { private Fragment mCurFragment; public static SlidingMenu mSlidingMenu; private Handler handler = new MyHandler(this); private static class MyHandler extends Handler { private final WeakReference<MainActivity> mActivity; public MyHandler(MainActivity activity) { mActivity = new WeakReference<MainActivity>(activity); } @Override public void handleMessage(Message msg) { MainActivity activity = mActivity.get(); if (activity == null) { return; } activity.handleMsg(msg); } } private void handleMsg(Message msg) { } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); if (savedInstanceState != null) { mCurFragment = getSupportFragmentManager().getFragment(savedInstanceState, "mCurContent"); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); getSupportFragmentManager().putFragment(outState, "mCurContent", mCurFragment); } private void initViews() {// 通过id 找到slidingmenu mSlidingMenu = (SlidingMenu) findViewById(R.id.slidingmenu); mSlidingMenu.setMenu(R.layout.sliding_left_frame);//给slidingmenu 添加左边的布局 if (getFragmentByTag(LeftFragment.class) == null) {//添加左边fragment getSupportFragmentManager().beginTransaction().add(R.id.left_frame, new LeftFragment(), LeftFragment.class.getName()).commit(); } mSlidingMenu.setContent(R.layout.sliding_center_frame);//添加一个空布局,后面承载 中间的fragment mSlidingMenu.setSecondaryMenu(R.layout.sliding_right_frame);//添加右面的布局,添加右边的fragment if (getFragmentByTag(RightFragment.class) == null) { getSupportFragmentManager().beginTransaction().add(R.id.right_frame, new RightFragment(), RightFragment.class.getName()).commit(); } if (mCurFragment != null) { postSwitchFragment(); } mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);//设置滑动模式,边缘 还是整个界面 } /** * slidingMenu中的内容Fragment切换(左侧菜单触发) * * @param clazz */ public void switchCenterFragment(Class<? extends Fragment> clazz) { try { if (mSlidingMenu == null) { removeAllFragments(); return; } boolean isInit = false; FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); Fragment userFragment = fm.findFragmentByTag(clazz.getName()); if (userFragment == null) { isInit = true; try { userFragment = clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } if (mCurFragment != null && mCurFragment != userFragment) { ft.hide(mCurFragment); } if (!userFragment.isAdded() && isInit) { ft.add(R.id.center_frame, userFragment, clazz.getName()); } else { ft.show(userFragment); } ft.commitAllowingStateLoss(); mCurFragment = userFragment; if (MainHallFragment.class.getName().equals(clazz.getName())) { mSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT); if (!isInit) { ((MainHallFragment) userFragment).postScrollTop(); } } else { mSlidingMenu.setMode(SlidingMenu.LEFT_RIGHT); } postShowContent(200); } catch (Exception e) { e.printStackTrace(); } } /** * lidingMenu中的内容Fragment内容过滤(右侧菜单触发) * * @param clazz * @param type */ public void filterCenterFragment(Class<? extends BaseSlidingFragment> clazz, int type) { BaseSlidingFragment userFragment = (BaseSlidingFragment) getFragmentByTag(clazz); if (userFragment != null) { userFragment.filter(type); } if (mSlidingMenu != null) mSlidingMenu.showContent(); } /** * 延迟切换Fragment */ private void postSwitchFragment() { handler.postDelayed(new Runnable() { @Override public void run() { switchCenterFragment(mCurFragment.getClass()); } }, 50); } /** * 清除FragmentManager中所有Fragment */ private void removeAllFragments() { FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); for (int i = 0; i < LeftFragment.FRAGMENTS_CLASSES.length; i++) { Fragment fragment = getFragmentByTag(LeftFragment.FRAGMENTS_CLASSES[i].getName()); if (fragment != null) { ft.remove(fragment); } } ft.commitAllowingStateLoss(); } /** * 延时mSlidingMenu.showContent() * * @param delayMillis 延时时间 单位毫秒 */ private void postShowContent(long delayMillis) { handler.postDelayed(new Runnable() { @Override public void run() { if (mSlidingMenu!=null && !MainActivity.this.isFinishing()) { mSlidingMenu.showContent(); } } }, delayMillis); } } ~~~ 左边的fragment ~~~ package com.os.activity.sliding; import java.util.Arrays; import com.os.activity.MainActivity; import com.os.activity.base.BaseSlidingFragment; import com.os.slidingmenu.R; import com.os.ui.FollowFragment; import com.os.ui.MainHallFragment; import com.os.ui.RankFragment; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView; import android.widget.Toast; public class LeftFragment extends BaseSlidingFragment { private final static int MENU_NORMAL_ICONS[] = { R.drawable.sliding_livehall_icon_normal, R.drawable.sliding_follow_icon_normal, R.drawable.sliding_rank_icon_normal}; private final static int MENU_CHECKED_ICONS[] = { R.drawable.sliding_livehall_icon_checked, R.drawable.sliding_follow_icon_checked, R.drawable.sliding_rank_icon_checked }; public final static Class[] FRAGMENTS_CLASSES = { MainHallFragment.class, FollowFragment.class, RankFragment.class};//左侧切换显示中间的三个界面 private View[] mMenuLayouts; private ImageView[] mMenuIcons; private TextView[] mMenuTexts; private Bitmap mLoadingBitmap; private int mCurrentIndex = -1; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sliding_left); setData(); changeMenuByClass(MainHallFragment.class);//默认中间的界面显示的是这个MainHallFragment } @Override public void initViews() { mMenuLayouts = new View[] { findViewById(R.id.menu_livehall_layout), findViewById(R.id.menu_follow_layout), findViewById(R.id.menu_rank_layout) }; mMenuIcons = new ImageView[] { (ImageView) findViewById(R.id.menu_livehall_icon), (ImageView) findViewById(R.id.menu_follow_icon), (ImageView) findViewById(R.id.menu_rank_icon) }; mMenuTexts = new TextView[] { (TextView) findViewById(R.id.menu_livehall_text), (TextView) findViewById(R.id.menu_follow_text), (TextView) findViewById(R.id.menu_rank_text),}; } @Override public void addListener() { for (int i = 0; i < mMenuLayouts.length; i++) { mMenuLayouts[i].setTag(i); mMenuLayouts[i].setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { int index = (Integer) v.getTag(); changeMenuByIndex(index); } }); } } private void setData() { } /** * 通过索引改变Menu * * @param index */ @SuppressWarnings("unchecked") private void changeMenuByIndex(int index) { Class<? extends Fragment> clazz = null; if (mCurrentIndex != index) { clearMenu(); setMenuChecked(index); } clazz = FRAGMENTS_CLASSES[index]; getFragmentActivity(MainActivity.class).switchCenterFragment(clazz); mCurrentIndex = index; } /** * 通过Fragment类改变menu * * @param clazz */ public void changeMenuByClass(Class<? extends Fragment> clazz) { int index = Arrays.asList(FRAGMENTS_CLASSES).indexOf(clazz); if (index != -1) { changeMenuByIndex(index); } } @SuppressWarnings("deprecation") private void clearMenu() { for (int i = 1; i <= mMenuLayouts.length; i++) { mMenuLayouts[i-1].setBackgroundDrawable(null); mMenuIcons[i - 1].setImageResource(MENU_NORMAL_ICONS[i - 1]); mMenuTexts[i - 1].setTextColor(getResources().getColor(R.color.gray7)); } } private void setMenuChecked(int index) { // if (index == 0) { // return; // } if (index != 1 && index != 2) { mMenuLayouts[index].setBackgroundResource(R.drawable.sliding_menu_checked_bg); } mMenuIcons[index ].setImageResource(MENU_CHECKED_ICONS[index ]); mMenuTexts[index ].setTextColor(getResources().getColor(R.color.white)); } @Override public void onDestroy() { if (mLoadingBitmap != null && !mLoadingBitmap.isRecycled()) { mLoadingBitmap.recycle(); mLoadingBitmap = null; } super.onDestroy(); } } ~~~ 主要就是 oncreate 中 初始化 刚启动应用后中间显示的fragment 是 MainHallFragment. 视频时RTMP 协议,解码用的ffmpeg. 代码就不贴了,在工程的jni 目录下,都有注释。 聊天的代码 聊天室界面是 ChatroomActivity. 这是 一初始化MultiUserChat 聊天室对象的代码,具体代码 ,在这个类里面。 ~~~ new Thread() { public void run() { try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } int count = 0; boolean isRandom = false; while (count <= 10 && !create_flag) { count++; try { if (mHostInfo != null) { mUserNickName = getNickName(isRandom); muc = ConnectionUtils.getMultiUserChat(mHostInfo.room_id, mHostInfo.room_service, mUserNickName, mPassword, ChatroomActivity.this); if (muc != null) { create_flag = true; // 创建聊天室成功,监听聊天室返回的消息 // 监听消息 muc.addMessageListener(packetListener); // muc.addParticipantListener(participantListener); muc.addParticipantStatusListener(statusListener); muc.addUserStatusListener(userStatusListener); mHandler.sendEmptyMessageDelayed(9, 500); } else { create_flag = false; } } else { create_flag = false; } // } else { // // } } catch (NotFoundException e) { e.printStackTrace(); } catch (SameException e) {// 昵称重复 isRandom = true; } catch (BannedException e) {// 禁止加入房间 sendBandHandle(15); return; } catch (XMPPException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } ~~~
';