android之仿qq

仿qq

1、环境准备

开发环境:
1.1、前端:Android Studio3.5.3spark客户端
1.2、后台:IDEA2019.2.4openfire

  1. Android Studio :qq页面;
  2. spark :与qq进行聊天的另一客户端;
  3. IDEA2019.2.4,采用springboot框架;
  4. openfire :实现即时聊天的服务器;

2、功能

2.1 应用功能

登录
手机号短信验证+账号进行注册
手机号短信验证+密码找回
与图灵机器人聊天
在线即时聊天
qq页面设计(Navigation、Drawerable、Toolbar、Adapter、switch button…)
动态栏页面列表的选择展示
意见反馈

2.2 采用的接口

图灵机器人
mob短信验证
openfire的xmpp协议
okhttp–基于websocket

3、项目结构

android

后台

  • 表user:
    表user
  • 表friend:
    表friend
  • 表feedback:
    表feedback
  • 表collect:
    表collect

4、gradle准备环境+其他准备

4.1. 短信验证

AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
app:
classpath 'com.mob.sdk:MobSDK:+'// 注册MobSDK
apply plugin: 'com.mob.sdk'
MobSDK {
    appKey "xxxxx"
    appSecret "xxxxxx"
    SMSSDK {}
}

4.2. okhttp

  • 我们一直使用的http协议只能由客户端发起,服务端无法直接进行推送,这就导致了如果服务端有持续的变化客户端想要获知就比较麻烦。WebSocket协议就是为了解决这个问题应运而生。
  • WebSocket协议,客户端和服务端都可以主动的推送消息,可以是文本也可以是二进制数据。而且没有同源策略的限制,不存在跨域问题。协议的标识符就是ws。像https一样如果加密的话就是wxs
AndroidManifest.xml:
<uses-permission  android:name="android.permission.INTERNET" />
dependencies :
implementation "com.squareup.okhttp:okhttp:2.7.5"
implementation 'com.squareup.okio:okio:2.4.1'
implementation 'com.alibaba:fastjson:1.2.62'

4.3、openfire和smack

3.1【 openfire的安装和准备
3.2 smack配置:
在这里插入图片描述

5、适配器+listview(此处以fragment为例)

	private ArrayList<String> qqpic;//头像
    private ArrayList<Object> qqname;//昵称
    private ArrayList<Object> qqqm;//个性签名
    private ListView friendList;
    private View root;
    
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    root = inflater.inflate(R.layout.fragment_dashboard, container, false);

        friendList = (ListView)root.findViewById(R.id.friendList);
        dataInit();
        ArrayAdapter<Object> tt = newMyAdapter(getActivity(),R.drawable.qq1,qqname,qqqm);
        friendList.setAdapter(tt);
        tt.notifyDataSetChanged();
        return root;
    }
    
    private void dataInit(){
        qqname = new ArrayList<Object>();
        qqname.add("qly");
        qqname.add("ljx");
        qqname.add("lucy");
        qqname.add("lily");
        qqname.add("lolo");
        qqname.add("fancy");
        qqname.add("koko");
        qqname.add("wwa");
        qqname.add("polo");

        qqqm = new ArrayList<Object>();
        qqqm.add("哗啦啦");
        qqqm.add("1111");
        qqqm.add("呜呜呜呜");
        qqqm.add("啊啊啊啊啊");
        qqqm.add("热热热热热热");
        qqqm.add("嗷嗷嗷啊啊");
        qqqm.add("踩踩踩从");
        qqqm.add("吱吱吱吱在");
        qqqm.add("十四说四十是");

        qqpic = new ArrayList<String>();
        qqpic.add(String.valueOf(R.drawable.qq1));
        qqpic.add(String.valueOf(R.drawable.qq2));
        qqpic.add(String.valueOf(R.drawable.qq3));
        qqpic.add(String.valueOf(R.drawable.qq4));
        qqpic.add(String.valueOf(R.drawable.qq5));
        qqpic.add(String.valueOf(R.drawable.qq6));
        qqpic.add(String.valueOf(R.drawable.qq7));
        qqpic.add(String.valueOf(R.drawable.qq8));
        qqpic.add(String.valueOf(R.drawable.qq9));
    }
    
    public class MyAdapter extends ArrayAdapter<Object> {//自定义适配器
        private Context context;
        private int resource;
        List<Object> object1;
        List<Object> object2;

        public MyAdapter(Context context, int resource, List<Object> object1, List<Object> object2) {
            super(context, resource, object1);
            this.context = context;
            this.resource = resource;
            this.object1 = object1;
            this.object2 = object2;
        }

        public MyAdapter(Context context, int resource) {
            super(context, resource);
        }

        public View getView(final int pos, View convertView, ViewGroup parent) {
            if (convertView == null)
                convertView = LayoutInflater.from(getActivity()).inflate(R.layout.friend, null);
            ImageView iv = (ImageView) convertView.findViewById(R.id.qqImg);
            iv.setImageResource(Integer.parseInt(qqpic.get(pos)));
            TextView name = (TextView) convertView.findViewById(R.id.qqName);
            name.setText(qqname.get(pos).toString());
            TextView qm = (TextView) convertView.findViewById(R.id.qqQm);
            qm.setText(qqqm.get(pos).toString());

            final Button bt = (Button) convertView.findViewById(R.id.chatBtn);

            bt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Toast.makeText(getActivity(), "click\n", Toast.LENGTH_SHORT).show();
                }
            });
            return convertView;
        }
    }

ListView:
在这里插入图片描述
效果:
在这里插入图片描述

6、抽屉栏和导航栏

继承自AppCompatActivity的Activity:

/////DrawerLayout/////////////////////////////
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
AppBarConfiguration m1 = new AppBarConfiguration.Builder(R.id.activity,
                R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow,
                R.id.nav_tools,R.id.feedback)
                .setDrawerLayout(drawer)
                .build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, m1);
 NavigationUI.setupWithNavController(navigationView, navController);
/////NavigationView + DrawerLayout////////////////
View headerView = navigationView.getHeaderView(0);//获取头布局
infoBtn = headerView.findViewById(R.id.infobtn);
infoBtn.setOnClickListener(new View.OnClickListener(){
       @Override
       public void onClick(View v) {
            System.out.println("-----------------");
            Intent intent1 = new Intent(MainActivity.this,MyInfoActivity.class);
            startActivity(intent1);
       }
});
////////////抽屉点击////////////////////
navigationView.setNavigationItemSelectedListener(new OnNavigationItemSelectedListener() {
       @Override
       public boolean onNavigationItemSelected(@NonNull MenuItem item) {
       		switch (item.getItemId()){
                    case R.id.activity://点我了解会员
                        Intent intent = new Intent(MainActivity.this, ShareAC.class);
                        startActivity(intent);
                        break;
                        ......
                        }
                   		return true;
            }
});


/////////bottom//////////////////////////
BottomNavigationView navView = findViewById(R.id.nav_view_main);
AppBarConfiguration m2 = new AppBarConfiguration.Builder(
                R.id.navigation_dashboard, R.id.navigation_home, R.id.navigation_notifications)
                .build();
NavigationUI.setupActionBarWithNavController(this, navController, m2);
NavigationUI.setupWithNavController(navView, navController);
activity_main_drawer.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single"
        android:clickable="true">
        <item
            android:id="@+id/activity"
            android:icon="@drawable/vip"
            android:title="点我了解会员" />
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/wallet"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/nav_gallery"
            android:icon="@drawable/fav"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/nav_slideshow"
            android:icon="@drawable/file"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/nav_tools"
            android:icon="@drawable/plug"
            android:title="@string/menu_tools" />

        <item
            android:id="@+id/feedback"
            android:icon="@drawable/feedback"
            android:title="意见反馈" />
    </group>

</menu>

效果图:
在这里插入图片描述

7、转换按钮

dependencies添加:
implementation 'com.github.zcweng:switch-button:0.0.3@aar'
extends AppCompatActivity implements SwitchButton.OnCheckedChangeListener:
SwitchButton qzoneBtn1;
qzoneBtn1 = (SwitchButton) findViewById(R.id.qzoneBtn1);
qzoneBtn1.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener(){
            @Override
            public void onCheckedChanged(SwitchButton view, boolean isChecked) {
                ....
            }
        });
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <include
            layout="@layout/headdtsz"
            android:layout_width="401dp"
            android:layout_height="wrap_content" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="66dp">

                <ImageView
                    android:id="@+id/imageView"
                    android:layout_width="74dp"
                    android:layout_height="match_parent"
                    app:srcCompat="@drawable/qzone" />

                <TextView
                    android:id="@+id/textView"
                    android:layout_width="202dp"
                    android:layout_height="33dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentTop="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginStart="94dp"
                    android:layout_marginLeft="94dp"
                    android:layout_marginTop="17dp"
                    android:layout_marginBottom="16dp"
                    android:text="好友动态"
                    android:textSize="12sp" />

                <com.suke.widget.SwitchButton
                    android:id="@+id/qzoneBtn1"
                    android:layout_width="64dp"
                    android:layout_height="40dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="15dp"
                    android:layout_marginBottom="12dp"
                    app:sb_background="@color/smssdk_common_line_gray"
                    app:sb_checkline_color="@color/smssdk_bg_gray"
                    app:sb_show_indicator="false" />

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="66dp">

                <ImageView
                    android:id="@+id/imageView2"
                    android:layout_width="74dp"
                    android:layout_height="match_parent"
                    app:srcCompat="@drawable/tencent_news" />

                <TextView
                    android:id="@+id/textView2"
                    android:layout_width="202dp"
                    android:layout_height="33dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentTop="true"
                    android:layout_marginStart="95dp"
                    android:layout_marginLeft="95dp"
                    android:layout_marginTop="16dp"
                    android:text="腾讯新闻"
                    android:textSize="12sp" />

                <com.suke.widget.SwitchButton
                    android:id="@+id/newsBtn1"
                    android:layout_width="64dp"
                    android:layout_height="40dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="15dp"
                    android:layout_marginBottom="12dp"
                    app:sb_background="@color/smssdk_common_line_gray"
                    app:sb_checkline_color="@color/smssdk_bg_gray"
                    app:sb_show_indicator="false" />

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="66dp">

                <ImageView
                    android:id="@+id/imageView3"
                    android:layout_width="74dp"
                    android:layout_height="wrap_content"
                    app:srcCompat="@drawable/baidumap" />

                <TextView
                    android:id="@+id/textView3"
                    android:layout_width="202dp"
                    android:layout_height="33dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginStart="91dp"
                    android:layout_marginLeft="91dp"
                    android:layout_marginBottom="15dp"
                    android:text="百度地图"
                    android:textSize="12sp" />

                <com.suke.widget.SwitchButton
                    android:id="@+id/mapBtn1"
                    android:layout_width="64dp"
                    android:layout_height="40dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="15dp"
                    android:layout_marginBottom="12dp"
                    app:sb_background="@color/smssdk_common_line_gray"
                    app:sb_checkline_color="@color/smssdk_bg_gray"
                    app:sb_show_indicator="false" />

            </RelativeLayout>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="66dp">

                <ImageView
                    android:id="@+id/imageView4"
                    android:layout_width="74dp"
                    android:layout_height="wrap_content"
                    app:srcCompat="@drawable/baidu" />

                <TextView
                    android:id="@+id/textView4"
                    android:layout_width="202dp"
                    android:layout_height="33dp"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentLeft="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginStart="90dp"
                    android:layout_marginLeft="90dp"
                    android:layout_marginBottom="16dp"
                    android:text="百度"
                    android:textSize="12sp" />

                <com.suke.widget.SwitchButton
                    android:id="@+id/searchBtn1"
                    android:layout_width="64dp"
                    android:layout_height="40dp"
                    android:layout_alignParentEnd="true"
                    android:layout_alignParentBottom="true"
                    android:layout_marginEnd="15dp"
                    android:layout_marginBottom="12dp"
                    app:sb_background="@color/smssdk_common_line_gray"
                    app:sb_checkline_color="@color/smssdk_bg_gray"
                    app:sb_show_indicator="false" />
            </RelativeLayout>

        </LinearLayout>
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

效果图:
在这里插入图片描述

8、okhttp的使用<以反馈为例>

在获取联系方式和反馈内容后,传给后台(post/get),通过handler获得反馈后的结果。(因为这里采用了线程)

//注意使用ip而非localhost或127.0.1
String feedbackUrl = "http://xxx.xxx.xxx.xxx:端口/feedback/insert?";
private static Handler handler;

	@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.advice);

        submit = (Button)findViewById(R.id.btn_submit);
        submit.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                advice = (EditText)findViewById(R.id.et_advice);
                final String content = advice.getText().toString();
                myPhone = (EditText)findViewById(R.id.phone);
                String phone = myPhone.getText().toString();

                final String addr = feedbackUrl + "phone=" + phone + "&content=" + content;
                System.out.println(addr);
                postFeedback(addr, content);
            }
        });

        handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
//                super.handleMessage(msg);
                switch (msg.what) {
                    case 0:
                        String ans = msg.getData().get("hasFeedback").toString();
                        if(ans.equals("true")) {
                            Toast.makeText(FeedbackActivity.this, "反馈成功", Toast.LENGTH_SHORT).show();
                            Intent intent = new Intent(FeedbackActivity.this, MainActivity.class);
                            startActivity(intent);
                        }
                        break;
                    default:
                        Toast.makeText(FeedbackActivity.this, "反馈失败,请稍后再试", Toast.LENGTH_SHORT).show();
                }
            }
        };

    }
    
	//这里采用了post,若是get则去掉.post()或换成.get()
    private void postFeedback(final String  addr, final String jsonString){
        final boolean flag = false;
        new Thread(new Runnable() {
            @Override
            public void run() {
                MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
                RequestBody body = RequestBody.create(mediaType, jsonString);
                OkHttpClient okHttpClient = new OkHttpClient();
                okHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);
                Request request = new Request.Builder()
                        .url(addr)
                        .post(body)
                        .build();

                okHttpClient.newCall(request).enqueue(new Callback() {
                    @Override
                    public void onFailure(Request request, IOException e) {
                        System.out.println("111-------fail-------------------------------------------");
                    }

                    @Override
                    public void onResponse(Response response) throws IOException {
                        Message msg=new Message();
                        msg.what=0;
                        Bundle bundle=new Bundle();
                        bundle.putString("hasFeedback",response.header("hasFeedback"));
                        msg.setData(bundle);
                        handler.sendMessage(msg);

                        System.out.println("feedback:"+response.header("hasFeedback")+"222----------success------------------------------------------");
                    }
                });
            }
        }).start();
    }

9、短信验证

下载官网的案例

10、图灵机器人

b站视频

11、与openfire交互

public class OpenfireConnect{
    private static XMPPTCPConnectionConfiguration config;
    private static XMPPTCPConnection conn;//AbstractXMPPConnection
    private static HttpServletResponse resp;
    private static String isLogin = "false";
    private ArrayList<String> msg_ans = new ArrayList<String>();
    private static int flag = 1;

    static BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    static BASE64Decoder decoder = new sun.misc.BASE64Decoder();

    static {
        try {
            config = XMPPTCPConnectionConfiguration.builder()
                    .setXmppDomain("自设服务器名称")
                    .setHost("自设主机名称") 
                    .setPort(5222)//.setDebuggerEnabled(true)
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                    .build();
            conn = new XMPPTCPConnection(config);
            conn.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @PostMapping("/conn")
    public static void login(@Param("username") String username,@Param("password") String password){
        try {
            if(conn!=null){
                conn.login(username, password);
                isLogin = "true";
                System.out.println("user "+username+" login successfully.");
            }
        } catch (XMPPException e) {
            e.printStackTrace();
        } catch (SmackException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @GetMapping("/send")
    public void sendMessage(@Param("username") String username, @Param("password") String password, @Param("friend")String friend, @Param("msg") String msg, HttpServletResponse response){
        if(isLogin.equals("false"))
            login(username, password);
        flag = 0;
        //再构建聊天室
        ChatManager cm = ChatManager.getInstanceFor(conn);
        cm.addIncomingListener((from, message, chat) -> {
            String ans = message.getBody();
            flag = 1;
            response.addHeader("msg",ans);
            msg_ans.add(ans);
            System.out.println(friend +":response-------,msg----------"+response.getHeader("msg"));
        });
        if(flag == 0){
            response.addHeader("msg",null);
            msg_ans.clear();
        }
        else if(flag == 1){
            response.addHeader("msg",msg_ans.toString());
        }
        System.out.println("response,msg----------"+response.getHeader("msg"));

        try {
            EntityBareJid jid = JidCreate.entityBareFrom(friend+"@fancycom.qq");//这里就是Xmpp地址,用户名@域名
            Chat chat = cm.chatWith(jid);
            Message message = new Message();
            message.setBody(msg);
            chat.send(message);
//            while (true);
        } catch (XmppStringprepException e) {
            e.printStackTrace();
        } catch (SmackException.NotConnectedException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述

发布了33 篇原创文章 · 获赞 2 · 访问量 6942

猜你喜欢

转载自blog.csdn.net/qq_42677329/article/details/103734473