android 练习之路

Posted Qunter

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 练习之路 相关的知识,希望对你有一定的参考价值。

项目的github地址:https://github.com/Qunter/SearchAndCall

------------------------------------------------------------------------

上图

PS:也不知道为什么,自从加了融云之后在虚拟机里使用的时候一直会报俩错,估计应该是跟6.0的适配有关吧,不过真机不报错,并且也不影响使用,那就先这样放着吧

PPS:一开始账号打错了,我还真是老年人记忆力......囧

今天使用Bmob来实现好友关系的存储,融云的IM来实现好友间的聊天

先做好准备工作,融云的依赖添加好,首先是下最新的kit包,然后导入两个module,并且添加依赖

    //融云IM依赖
    compile project(\':IMKit\')

这里还有一些相关的设置,暂且不说,官网上有文档

这还不算完,因为这个程序里我们没有后台(竞赛提交作品要能直接演示,当然了,客观原因是我也不是很喜欢挂后台,挺麻烦的),所以我们得在客户端里实现后台的功能,所以我们需要导入一些融云的工具类

它们在哪下载呢,可以下载一个java的融云的后台demo,然后把其中的io.rong包复制过来,粘贴到项目里

这就算把先头工作做的差不多了,然后一步一步来

还是类似上一个活动模块的布局样式

activity_friend_info_fragm.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.design.widget.TabLayout
        android:id="@+id/friendTabLayout"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        app:tabIndicatorColor="@android:color/black"/>
    <android.support.v4.view.ViewPager
        android:id="@+id/friendViewPaper"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>

布局完了就是跟一个Activity

FriendInfoFragmActivity.java

public class FriendInfoFragmActivity extends Fragment {
    public static FriendInfoFragmActivity newInstance() {
        return new FriendInfoFragmActivity();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_friend_info_fragm, container, false);
        //Fragment+ViewPager+FragmentViewPager组合的使用
        ViewPager viewPager = (ViewPager) view.findViewById(R.id.friendViewPaper);
        FriendPagerAdapter adapter = new FriendPagerAdapter(getActivity().getSupportFragmentManager(), getContext());
        viewPager.setAdapter(adapter);
        //TabLayout
        TabLayout tabLayout = (TabLayout) view.findViewById(R.id.friendTabLayout);
        tabLayout.setupWithViewPager(viewPager);
        return view;
    }
}

接着是建一个viewpager的adapter

FriendPagerAdapter.java

public class FriendPagerAdapter extends FragmentPagerAdapter {
    public final int COUNT = 2;
    private String[] titles = new String[]{"消息列表", "我的好友"};
    private Context context;
    private Fragment conversationList;
    private Fragment conversationFragment = null;
    public FriendPagerAdapter(FragmentManager fm, Context context) {
        super(fm);
        this.context = context;
    }

    @Override
    public Fragment getItem(int position) {
        if(position==0){
            conversationList = initConversationList();//获取融云会话列表的对象
            return conversationList;
        }else if(position==1){
            return FriendPageFragment.newInstance();
        }
        return null;
    }

    @Override
    public int getCount() {
        return COUNT;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles[position];
    }
    private Fragment initConversationList() {

        /**
         * appendQueryParameter对具体的会话列表做展示
         */
        if (conversationFragment == null) {
            ConversationListFragment listFragment = new ConversationListFragment();
            Uri uri = Uri.parse("rong://" + context.getPackageName()).buildUpon()
                    .appendPath("conversationList")
                    .appendQueryParameter(Conversation.ConversationType.PRIVATE.getName(), "false")//设置私聊会话是否聚合显示
                    .appendQueryParameter(Conversation.ConversationType.GROUP.getName(), "true")
                    // .appendQueryParameter(Conversation.ConversationType.PUBLIC_SERVICE.getName(), "false")//公共服务号
                    //.appendQueryParameter(Conversation.ConversationType.APP_PUBLIC_SERVICE.getName(), "false")//公共服务号
                    .appendQueryParameter(Conversation.ConversationType.DISCUSSION.getName(), "false")//设置私聊会话是否聚合显示
                    .appendQueryParameter(Conversation.ConversationType.SYSTEM.getName(), "false")//设置私聊会是否聚合显示
                    .build();
            listFragment.setUri(uri);
            return listFragment;
        } else {
            return conversationFragment;
        }
    }
}

左边是消息列表,这个用融云的消息列表,右边是好友列表,就需要我们自己定义了

从左至右吧

融云的消息列表

activity_conversation.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#000">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_margin="15dp"
            android:text="rongCloud"
            android:textColor="#fff"
            android:textSize="18sp" />

    </LinearLayout>
    <fragment
        android:id="@+id/conversation"
        android:name="io.rong.imkit.fragment.ConversationFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</LinearLayout>

然后是Activity

(不是我说,这个命名是真的有毒,这个类是从融云提供的demo上摘下来的,不过反正是他们内部实现的东西,先不改得了,忙完这两天再一起弄)

public class ConversationActivity extends FragmentActivity {

    private TextView mName;
    //private ImageView mPersionInformationIv;
    private Conversation.ConversationType mConversationType;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_conversation);
        Intent intent = getIntent();
        mConversationType = Conversation.ConversationType.valueOf(intent.getData().getLastPathSegment().toUpperCase(Locale.getDefault()));
        //Log.e("log", mConversationType.getName());
        mName = (TextView) findViewById(R.id.name);
        /*
        mPersionInformationIv = (ImageView) findViewById(R.id.persionInformationIv);
        if(mConversationType.getName().equals("private")){
            mPersionInformationIv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent mIntent = new Intent(getApplicationContext(),UserInformationPage.class);
                    Bundle bundle=new Bundle();
                    bundle.putString("userNickName", getIntent().getData().getQueryParameter("title"));
                    mIntent.putExtras(bundle);
                    startActivity(mIntent);
                }
            });
        }else if(mConversationType.getName().equals("discussion")){
            mPersionInformationIv.setVisibility(View.GONE);
        }
        */
        String sId = getIntent().getData().getQueryParameter("targetId");//targetId:单聊即对方ID,群聊即群组ID
        String sName = getIntent().getData().getQueryParameter("title");//获取昵称
        if (!TextUtils.isEmpty(sName)){
            mName.setText(sName);
        }else {
//            sId
            //TODO 拿到id 去请求自己服务端
        }
    }
}

然后这个界面注册的方式不一样,这里也贴一下,其实官方文档有写

     <!--会话界面-->
        <activity
            android:name=".activity.ConversationActivity"
            android:screenOrientation="portrait"
            android:windowSoftInputMode="stateHidden|adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data
                    android:host="com.qunter.searchcall"
                    android:pathPrefix="/conversation/"
                    android:scheme="rong" />
            </intent-filter>
        </activity>

 然后就是好友列表的实现

activity_friend_list.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
    android:id="@+id/swipe_refresh_friend"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_friend"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:overScrollMode="never"
        android:scrollbars="vertical" />
</android.support.v4.widget.SwipeRefreshLayout>

item_friend_info.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView android:id="@+id/friend_card_view"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_marginBottom="2dp"
    android:layout_marginTop="2dp"
    android:clickable="true"
    app:cardBackgroundColor="@color/card_view_background_dark"
    app:cardCornerRadius="0dp"
    app:cardElevation="2dp">

    <LinearLayout
        android:id="@+id/item_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/item_useravatar"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="10dp"
            android:scaleType="fitCenter"
            android:src="@drawable/account_avatar" />
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/item_username"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_marginEnd="10dp"
                android:lineSpacingExtra="2dp"
                android:maxLines="1"
                android:padding="2dp"
                android:textColor="@color/font_normal"
                android:textSize="18sp" />
        </RelativeLayout>


    </LinearLayout>
</android.support.v7.widget.CardView>

左边直接new一个融云的会话列表就行,但是右边需要自己写

FriendPageFragment.java

public class FriendPageFragment extends Fragment implements RongIM.UserInfoProvider{
    private SwipeRefreshLayout friendSwipeRefreshLayout;
    private FriendInfoListAdapter adapter;
    private List<UserInfo> loginUserFriendInfoList;
    private List<Friend> loginUserFriendRongList;
    private RecyclerView friendRecyclerView;
    private String loginUserFriendObjectID="";
    private final int GETLOGINUSERFRIENDOBJECTID=0x00,GETLOGINUSERFRIENDLIST=0x01,FRIENDINFORMATIONLISTDOWNOVER=0x02;
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case GETLOGINUSERFRIENDOBJECTID:
                    getLoginUserFriendObjectID();
                    break;
                case GETLOGINUSERFRIENDLIST:
                    getLoginUserFriendList();
                    break;
                case FRIENDINFORMATIONLISTDOWNOVER:
                    loadFriendRecycleView();
                    initUserInfo();
                    friendSwipeRefreshLayout.setRefreshing(false);
                    break;

            }
        }
    };
    public static FriendPageFragment newInstance() {
        FriendPageFragment fragment = new FriendPageFragment();
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.activity_friend_list,container,false);
        friendSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh_friend);
        friendSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                handler.sendEmptyMessage(GETLOGINUSERFRIENDLIST);
            }
        });
        friendRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_friend);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        friendRecyclerView.setLayoutManager(layoutManager);
        handler.sendEmptyMessage(GETLOGINUSERFRIENDOBJECTID);
        return view;
    }
    /**
     * 获取用户好友列表的数据库表单中ID
     */
    private void getLoginUserFriendObjectID(){
        final BmobQuery<UserFriend> userObjectIDQuery = new BmobQuery<UserFriend>();
        userObjectIDQuery.addWhereEqualTo("userID", BmobUser.getCurrentUser(UserInfo.class).getUserPhone());
        userObjectIDQuery.findObjects(new FindListener<UserFriend>() {
            @Override
            public void done(List<UserFriend> object, BmobException e) {
                if(e==null){
                    loginUserFriendObjectID =object.get(0).getObjectId();
                    //Toast.makeText(getContext(), "loginUserFriendObjectID"+loginUserFriendObjectID, Toast.LENGTH_SHORT).show();
                    handler.sendEmptyMessage(GETLOGINUSERFRIENDLIST);
                }else{
                }
            }
        });

    }
    /**
     *获取用户好友列表数据
     */
    private void getLoginUserFriendList(){
        UserFriend userFriend = new UserFriend();
        // 查询好友列表内的所有用户,因此查询的是用户表
        BmobQuery<UserInfo> userFriendQuery = new BmobQuery<UserInfo>();
        userFriend.setObjectId(loginUserFriendObjectID);
        //userFriend是UserFriend表中的字段,用来存储所有该用户的好友关系的用户
        userFriendQuery.addWhereRelatedTo("userFriend", new BmobPointer(userFriend));
        userFriendQuery.findObjects(new FindListener<UserInfo>() {
            @Override
            public void done(List<UserInfo> object,BmobException e) {
                if(e==null){
                    loginUserFriendInfoList=object;
                    Log.e("taggg", loginUserFriendInfoList.size()+"" );
                    Log.e("taggg", loginUserFriendInfoList.get(0).getUserNickname()+"" );
                    //Toast.makeText(getContext(), "成功加载好友列表数据"+userFriendInformationList.size(), Toast.LENGTH_SHORT).show();
                    handler.sendEmptyMessage(FRIENDINFORMATIONLISTDOWNOVER);
                }else{
                    Toast.makeText(getContext(), "加载好友列表数据失败"+e, Toast.LENGTH_SHORT).show();
                }
            }

        });
    }
    /**
     * 加载好友信息至RecycleView
     */
    private void loadFriendRecycleView(){
        adapter = new FriendInfoListAdapter(getContext(),loginUserFriendInfoList,friendRecyclerView);
        adapter.setOnItemClickListener(new FriendInfoListAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                RongIM.getInstance().startPrivateChat(getContext(), loginUserFriendInfoList.get(position).getUserPhone(), loginUserFriendInfoList.get(position).getUserNickname());
            }
        });
        friendRecyclerView.setAdapter(adapter);
    }
    /**
     * 储存好友相关信息:id,昵称,token
     */
    private void initUserInfo() {
        loginUserFriendRongList = new ArrayList<Friend>();
        loginUserFriendRongList.add(new Friend(BmobUser.getCurrentUser(UserInfo.class).getUserPhone(),BmobUser.getCurrentUser(UserInfo.class).getUserNickname(),BmobUser.getCurrentUser(UserInfo.class).getUserAvatar()));
        for(UserInfo userFriendInfo:loginUserFriendInfoList){
            //好友内容为:id,昵称,头像url
            loginUserFriendRongList.add(new Friend(userFriendInfo.getUserPhone(),userFriendInfo.getUserNickname(),userFriendInfo.getUserAvatar()));
        }
        RongIM.setUserInfoProvider(this, true);
    }
    @Override
    public io.rong.imlib.model.UserInfo getUserInfo(String s) {
        for (Friend i : loginUserFriendRongList) {
            if (i.getUserId().equals(s)) {
                //Log.e(TAG, i.getPortraitUri());
                return new io.rong.imlib.model.UserInfo(i.getUserId(), i.getName(), Uri.parse(i.getPortraitUri()));
            }
        }
        return null;
    }
}

然后就需要写一个RecycleView的Adapter了

FriendInfoListAdapter.java

public class FriendInfoListAdapter extends RecyclerView.Adapter<FriendInfoListAdapter.ViewHolder>{
    private List<UserInfo> loginUserFriendInfoList;
    private Context context;
    private RecyclerView recyclerView;
    private OnItemClickListener onItemClickListener;

    static class ViewHolder extends RecyclerView.ViewHolder{
        private ImageView userAvatarImg;
        private TextView userNameTv;
        public ViewHolder(View view){
            super(view);
            userAvatarImg = (ImageView) view.findViewById(R.id.item_useravatar);
            userNameTv = (TextView) view.findViewById(R.id.item_username);
        }
    }
    public FriendInfoListAdapter(Context context,List<UserInfo> loginUserFriendInfoList,RecyclerView recyclerView){
        this.context = context;
        this.loginUserFriendInfoList = loginUserFriendInfoList;
        this.recyclerView = recyclerView;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_friend_info,parent,false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(final FriendInfoListAdapter.ViewHolder holder, final int position) {
        holder.setIsRecyclable(false);
        final UserInfo friendInfo = loginUserFriendInfoList.get(position);
        Glide.with(context).load(friendInfo.getUserAvatar()).bitmapTransform(new CropCircleTransformation(context)).into(holder.userAvatarImg);
        holder.userNameTv.setText(friendInfo.getUserNickname());
        if(onItemClickListener != null){
            //为ItemView设置监听器
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = holder.getLayoutPosition();
                    onItemClickListener.onItemClick(holder.itemView,position);
                }
            });
        }
    }
    @Override
    public int getItemCount() {
        return loginUserFriendInfoList.size();
    }
    /**
     * 点击事件接口
     */
    public interface OnItemClickListener{
        void onItemClick(View view, int position);
    }

    /**
     * 设置点击事件方法
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener = onItemClickListener;
    }
}

然后回头看一眼,首先在MainActivity里应该初始化融云的服务

        RongIM.init(this);
        initUserToken(); 
   /**
     * 获取融云所需userID及token
     */
    private void initUserToken(){
        UserInfo loginUser = BmobUser.getCurrentUser(UserInfo.class);
        userID = loginUser.getUserPhone();
        connectRongServer(loginUser.getRongToken());
    }

    /**
     * 初始化登录用户的融云服务
     */
    private void connectRongServer(String token) {

        RongIM.connect(token, new RongIMClient.ConnectCallback() {
            @Override
            public void onSuccess(String userId) {
                if (userId.equals(userID)){
                    Toast.makeText(getApplicationContext(), userID+"成功连接", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(getApplicationContext(), userID+"连接失败", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onError(RongIMClient.ErrorCode errorCode) {
                // Log.e("onError", "onError userid:" + errorCode.getValue());//获取错误的错误码
                Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
                Log.e("MainActivity", "connect failure errorCode is : " + errorCode.getValue());
            }


            @Override
            public void onTokenIncorrect() {
                Toast.makeText(getApplicationContext(), "TokenError", Toast.LENGTH_SHORT).show();
                Log.e("MainActivity", "token is error ,please check token and appkey");
            }
        });

    }

实体类也需要完善一下了,我们现在还有很多其他需要的字段,以及融云所需的好友实体

UserInfo.java

public class UserInfo extends BmobUser {
    private String userNickname;
    private String userPhone;
    private String rongToken;
    //用户头像,先设置初始头像
    private String userAvatar="http://bmob-cdn-8854.b0.upaiyun.com/2017/01/21/910615c0405f9bd280350b57f8dc180c.png";

    public String getUserNickname() {
        return userNickname;
    }

    public void setUserNickname(String userNickname) {
        this.userNickname = userNickname;
    }

    public String getUserPhone() {
        return userPhone;
    }

    public void setUserPhone(String userPhone) {
        this.userPhone = userPhone;
    }

    public String getRongToken() {
        return rongToken;
    }

    public void setRongToken(String rongToken) {
        this.rongToken = rongToken;
    }

    public String getUserAvatar() {
        return userAvatar;
    }

    public void setUserAvatar(String userAvatar) {
        this.userAvatar = userAvatar;
    }
}

UserFriend.java

public class UserFriend extends BmobObject {
    private String userID;
    private BmobRelation userFriend;


    public BmobRelation getUserFriend() {
        return userFriend;
    }

    public void setUserFriend(BmobRelation userFriend) {
        this.userFriend = userFriend;
    }

    public String getUserID() {
        return userID;
    }

    public void setUserID(String userID) {
        this.userID = userID;
    }
}

Friend.java

public class Friend {

    private String userId;
    private String name;
    private String portraitUri;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPortraitUri() {
        return portraitUri;
    }

    public void setPortraitUri(String portraitUri) {
        this.portraitUri = portraitUri;
    }

    public Friend(String userId, String name, String portraitUri) {
        this.userId =

以上是关于android 练习之路 的主要内容,如果未能解决你的问题,请参考以下文章

android 练习之路

android 练习之路

android 练习之路

android 练习之路

屏幕旋转后Android片段重叠

Android代码片段