Android小项目——社交类app(低仿微信)

  • 一、项目简介
    • · 前言
    • · 项目框架
    • · 网络传输
  • 二、主要功能及代码分析
    • · 注册和登录
    • · 整体布局
    • · 搜索和关注好友
    • · 浏览动态(点赞评论收藏)
    • · 发布动态
    • · 个人信息
  • 三、总结
  • 四、源码下载

一、项目简介

· 前言

暑假参与了一个移动端Android应用开发的项目,原本打算做新闻类的,但是做着做着就跑偏了最后做成了社交类哈哈哈,所以包名一直没改,还是News。记录一下这个安卓项目,第一次上手,有问题请多多指教哦。

· 项目框架

· 网络传输

我们采用okhttp3的网络方式与服务器进行数据交互,okhttp3是一个高性能的http库,支持http2,对一台机器的所有请求共享同一个socket,通过缓存避免重复的请求。将数据打包成Jason格式进行传输。构造函数是OkhttpClient()。

二、主要功能及代码分析

· 注册和登录

初始界面:用户选择登录or注册

注册界面&登录界面

登录和注册功能的实现主要使用okhttp3方式与服务器进行交互,两者的技术实现完全一样。这里登录失败或者注册成功/失败都需要跳转到一个通知界面,所以要用到Handler机制,Handler是线程间的消息机制,Android中只有主线程才能修改UI界面,为了避免ANR(程序无响应),有时需要把一些耗时操作(网络请求、复杂计算)交给子线程完成,若子线程的执行需要修改UI界面时,就需要通知主线程,在Android中利用Handler机制可以实现这个通知。

登录——get用户信息,向服务器发送登录请求

String jsonstr = new Gson().toJson(hostInfo); //数据打包为Jason格式RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);OkHttpClient client = new OkHttpClient();//向服务器发送请求Request request = new Request.Builder().url("http://192.168.43.121:8080/login").post(body).build();//这里的http://192.168.43.121:8080就是我们服务器的url,login是为登录创建的存储文件夹client.newCall(request).enqueue(new Callback() {  //收到服务器回复    @Override    public void onFailure(Call call, IOException e) {        e.printStackTrace();    }    @Override    public void onResponse(Call call, Response response) throws IOException {        String back = response.body().string();        if(back.equals("success")){  //若收到服务器的回复为success,则成功。(这个success是你与服务器约定好的一个字符串)            Intent intent = new Intent(LoginActivity.this,MainActivity.class);            startActivity(intent);        }else if(back.equals("fail")){            Handler handler=new Handler(Looper.getMainLooper());            handler.post(new Runnable(){                public void run(){                    Toast.makeText(LoginActivity.this,"手机号码或密码错误,请重新输入!",Toast.LENGTH_SHORT).show();                }            });        }    }});

注册——get用户信息,向服务器发送注册请求

String jsonstr = new Gson().toJson(hostInfo);     //数据打包成Jason格式RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("http://192.168.43.121:8080/insert").post(body).build();client.newCall(request).enqueue(new Callback() {    @Override    public void onFailure(Call call, IOException e) {        Log.v("call","fail");    }    @Override    public void onResponse(Call call, Response response) throws IOException {        String back = response.body().string();        if(back.equals("success")){            Handler handler=new Handler(Looper.getMainLooper());            handler.post(new Runnable(){                public void run(){                    Toast.makeText(RegisterActivity.this,"注册成功,请重新登录!",Toast.LENGTH_SHORT).show();                }            });            Intent nextIntent = new Intent(RegisterActivity.this, LoginActivity.class);            startActivity(nextIntent);            finish();        }else if(back.equals("fail")){            Handler handler=new Handler(Looper.getMainLooper());            handler.post(new Runnable(){                public void run(){                    Toast.makeText(RegisterActivity.this,"注册失败,请重试!",Toast.LENGTH_SHORT).show();                }            });        }    }});

提醒: 注册的上传头像功能我用了一个第三方工具:PictureSelector,因为自己写的莫名报错,还是建议用第三方实现,它功能特别强大,可以选图、裁剪、设为圆形、正方形、等等,第三方工具在后面会给出链接。

· 整体布局

登录成功以后进入主界面——动态页面,侧滑栏为 个人信息

列表的布局采用ListView、数据源和Adapter配合实现。

//侧滑菜单private void initView(){ drawerLayout = findViewById(R.id.activity_na); navigationView = findViewById(R.id.nav); menu = findViewById(R.id.iv_menu); View headerView = navigationView.getHeaderView(0); user_image = headerView.findViewById(R.id.iv_menu_user); user_name = headerView.findViewById(R.id.tv_menu_user); user_sign = headerView.findViewById(R.id.tv_menu_usersign); new Thread(new Runnable() {     @Override     public void run() {         try{             MyselfUtil rec = new MyselfUtil();             MyInfo = rec.httpGet(hostID);             handlerPra.sendMessage(handlerPra.obtainMessage(22,MyInfo));         }catch (Exception e){             e.printStackTrace();         }     } }).start(); //获取头布局 menu.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View view) {         //点击菜单,跳出侧滑菜单         if (drawerLayout.isDrawerOpen(navigationView)){             drawerLayout.closeDrawer(navigationView);         }else{             drawerLayout.openDrawer(navigationView);         }     } }); navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {     @Override     public boolean onNavigationItemSelected(@NonNull MenuItem item) {         switch(item.getItemId()){             case R.id.menu_item1:                 Intent intent  = new Intent(MainActivity.this,MyCollectActivity.class);                 startActivity(intent);                 break;             case R.id.menu_item2:                 Intent intent2  = new Intent(MainActivity.this,MyLikeActivity.class);                 startActivity(intent2);                 break;             case R.id.menu_item3:                 Intent intent3  = new Intent(MainActivity.this,CommentActivity.class);                 startActivity(intent3);                 break;             case R.id.menu_item4:                 Intent intent4  = new Intent(MainActivity.this,MyMomentActivity.class);                 startActivity(intent4);                 break;         }         return true;     } });}

· 搜索和关注好友


查询好友id:
将id提交给服务器查询,根据收到的其回复来判断是否存在该用户

private void search_fd(final EditText friendID){    Button search_btn = findViewById(R.id.follow_search_btn);    search_btn.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View view) {            int friend_int = Integer.parseInt(friendID.getText().toString());            foucs_friend ffd = new foucs_friend(hostID,friend_int);            String jsonstr = new Gson().toJson(ffd);            RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);            OkHttpClient client = new OkHttpClient();            Request request = new Request.Builder()                    .url("http://192.168.43.121:8080/findfriend")                    .post(body)                    .build();            client.newCall(request).enqueue(new Callback() {                @Override                public void onFailure(Call call, IOException e) {                }                @Override                public void onResponse(Call call, Response response) throws IOException {                    String json;                    json = response.body().string();                    Gson gson = new Gson();                    Log.v("aaa",json);                    follow_fd = gson.fromJson(json,FocusInfo.class);                    Message message = new Message();                    message.what = 0;                    handlerPra.sendMessage(message);                }            });        }    });  }

关注好友:
将数据提交给服务器,服务器将关注信息保存到 MySQL 数据库中

private void follow_fd(final EditText friendID) {    btn_focus.setOnClickListener(new View.OnClickListener() {        @Override        public void onClick(View view) {            int friend_int = Integer.parseInt(friendID.getText().toString());            //int friend_int  = Integer.parseInt(friendID);            foucs_friend ffd = new foucs_friend(hostID,friend_int);            String jsonstr = new Gson().toJson(ffd);            RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);            OkHttpClient client = new OkHttpClient();            Request request = new Request.Builder()                    .url("http://192.168.43.121:8080/friendadd")                    .post(body)                    .build();            client.newCall(request).enqueue(new Callback() {                @Override                public void onFailure(Call call, IOException e) {                }                @Override                public void onResponse(Call call, Response response) throws IOException {                    String back = response.body().string();                    if(back.equals("success")){                        Handler handler=new Handler(Looper.getMainLooper());                        handler.post(new Runnable(){                            public void run(){                                btn_focus.setVisibility(View.INVISIBLE );                                focus_tv.setVisibility(View.VISIBLE );                                Toast.makeText(FollowActivity.this,"已关注",Toast.LENGTH_SHORT).show();                            }                        });                    }                }            });        }    });}

· 浏览动态(点赞评论收藏)


点赞、评论和收藏全部放在一个 Fragment 中,点赞和收藏可以直接向服务器发送请求,但是评论需要一个单独的Activity来get用户输入的具体评论信息,在那个Activity里面再进行数据上传,Fragment里面只调用这个Activity即可。

//在Fragment中实现点赞、评论和收藏功能//收藏adapter.setOnItemCollectListener(new ViewAdapter.onItemCollectListener() {    @Override    public void onCollectClick(final int i) {        PraiseOrCollectMsg msg = new PraiseOrCollectMsg();        msg.setDynamicID(list_item.get(i).getDynamicID());        msg.setHostID(1);        String jsonstr = new Gson().toJson(msg);        //向服务器发送请求        RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);        OkHttpClient client = new OkHttpClient();        Request request = new Request.Builder()                .url("http://192.168.43.121:8080/findcollect")                .post(body)                .build();        client.newCall(request).enqueue(new okhttp3.Callback() {            @Override            public void onFailure(Call call, IOException e) {            }            @Override            public void onResponse(Call call, Response response) throws IOException {                String json = response.body().string();                Log.v("accepet",json);                list_item.get(i).setIsCollected(Integer.parseInt(json));                Message message = new Message();                message.what = 0;                handlerPra.sendMessage(message);            }        });    }});//评论adapter.setOnItemCommentClickListener(new ViewAdapter.onItemCommentListener() {    @Override    public void onCommentClick(int i) {             System.out.println("");        comment(i);     //调用评论功能的Activity    }});//点赞adapter.setOnItemPraiseClickListener(new ViewAdapter.onItemPraiseListener() {    @Override    public void onPraiseClick(final int i) {        PraiseOrCollectMsg msg = new PraiseOrCollectMsg();        msg.setDynamicID(list_item.get(i).getDynamicID());        msg.setHostID(1);        String jsonstr = new Gson().toJson(msg);        RequestBody body = RequestBody.create(MediaType.parse("application/json"),jsonstr);        OkHttpClient client = new OkHttpClient();        //向服务器发送请求        Request request = new Request.Builder()                .url("http://192.168.43.121:8080/findpraise")                .post(body)                .build();        client.newCall(request).enqueue(new okhttp3.Callback() {            @Override            public void onFailure(Call call, IOException e) {            }            @Override            public void onResponse(Call call, Response response) throws IOException {                String json = response.body().string();                Gson gson = new Gson();                PraiseDetail pra = gson.fromJson(json,PraiseDetail.class);                list_item.get(i).setHasPraised(pra.haspriased);                list_item.get(i).setIsPraised(pra.isprased);                Message message = new Message();                message.what = 0;                handlerPra.sendMessage(message);            }        });    }});

· 发布动态


把发布动态功能放在Fragment里面,发布功能实现还是利用OKHTTP3网络传输。图片上传仍使用第三方工具。
我这里放一下 第三方工具包:PictureSelector

private void showAlbum() {    //参数很多,根据需要添加    PictureSelector.create(getActivity())        .openGallery(PictureMimeType.ofImage())// 全部.PictureMimeType.ofAll()、图片.ofImage()、视频.ofVideo()、音频.ofAudio()        .maxSelectNum(maxSelectNum)// 最大图片选择数量        .minSelectNum(1)// 最小选择数量        .imageSpanCount(4)// 每行显示个数        .selectionMode(PictureConfig.MULTIPLE)// 多选 or 单选PictureConfig.MULTIPLE : PictureConfig.SINGLE        .previewImage(true)// 是否可预览图片        .isCamera(true)// 是否显示拍照按钮        .isZoomAnim(true)// 图片列表点击 缩放效果 默认true        //.setOutputCameraPath("/CustomPath")// 自定义拍照保存路径        .enableCrop(true)// 是否裁剪        .compress(true)// 是否压缩        //.sizeMultiplier(0.5f)// glide 加载图片大小 0~1之间 如设置 .glideOverride()无效        .glideOverride(160, 160)// glide 加载宽高,越小图片列表越流畅,但会影响列表图片浏览的清晰度        .withAspectRatio(1, 1)// 裁剪比例 如16:9 3:2 3:4 1:1 可自定义        //.selectionMedia(selectList)// 是否传入已选图片        //.previewEggs(false)// 预览图片时 是否增强左右滑动图片体验(图片滑动一半即可看到上一张是否选中)        //.cropCompressQuality(90)// 裁剪压缩质量 默认100        //.compressMaxKB()//压缩最大值kb compressGrade()为Luban.CUSTOM_GEAR有效        //.compressWH() // 压缩宽高比 compressGrade()为Luban.CUSTOM_GEAR有效        //.cropWH()// 裁剪宽高比,设置如果大于图片本身宽高则无效        .rotateEnabled(false) // 裁剪是否可旋转图片        .scaleEnabled(true)// 裁剪是否可放大缩小图片        //.recordVideoSecond()//录制视频秒数 默认60s        .forResult(PictureConfig.CHOOSE_REQUEST);//结果回调onActivityResult code}

· 个人信息


对于收到的点赞/评论/我的收藏,均使用 Handler机制 开启一个新的线程,搭配适配器进行页面显示。

我收到的点赞:

Handler handler = new Handler(){    @Override    public void handleMessage(@NonNull Message msg) {        lv.setAdapter(new LikeAdapter(MyLikeActivity.this,(List<LikeInfo>) msg.obj));    }};

我收到的评论:

Handler handlerPra = new Handler(){    @Override    public void handleMessage(Message msg){        if(msg.what == 0){            adapter.notifyDataSetChanged();        }    }};

我的收藏:

Handler handlerPra = new Handler(){    @Override    public void handleMessage(Message msg){        if(msg.what == 0){            adapter.notifyDataSetChanged();        }    }};

三、总结

整个项目大致的主要功能就是如此啦,技术上难度不算太大,每个功能的实现都是在重复地使用OKHTTP3与服务器进行数据交互,必要时使用Fragment代替Activity,列表显示就结合Adapter来实现,我遇到最大的问题倒是在图片上传那里,所以我强烈建议使用第三方,拍照、选择图片、图片裁剪、缩放等等,功能很全了。这次的安卓开发项目完成度比较高,功能低仿微信,在真机上运行没有太大的问题,算是一次小小的突破。不过中间有些bug,是在整合的时候出现的,由于时间有限我们没有太追究这些细枝末节,仅作学习参考。欢迎大家和我一起交流!

四、源码下载

本项目源码在此开放给大家,请只作学习参考~

  • CSDN下载:
    Android小项目——社交类app(低仿微信)客户端源码下载
    Android小项目——社交类app(低仿微信)服务器端源码下载

  • github下载:
    客户端源码github链接
    服务端源码github链接

更多相关文章

  1. Android:调用系统图库/裁剪图片
  2. 安卓图片反复压缩后为什么普遍会变绿而不是其它颜色?
  3. Android中圆形图的几种实现方式
  4. Android实现图片相似度
  5. Android(安卓)滑动切换(首页展示,图片、新闻自动切换,循环切换,自动
  6. [置顶] Android中调用系统相机、系统相册来获取图片,并裁剪图片。
  7. 自定义Android带图片的按钮
  8. Android多点触控技术实战 针对图片自由缩放和移动
  9. Android(安卓)TCP通信的简单实例以及常见问题[超时/主线程阻塞]

随机推荐

  1. [Android] ImageView.ScaleType设置图解
  2. 创业公司招人才
  3. ERROR: All flavors must now belong to
  4. Android中Adapter中edittext,checkbox记住
  5. Android这四个你不可不知的知识点,你都了
  6. Android SharedPreferences应用解析
  7. android 笔记----禁止横屏和竖屏切换
  8. Android如何查看应用签名信息
  9. android cocos2d-x for Android安装和学
  10. Android多点触摸实现