RecyclerViewItemTouchHelperDemo【使用ItemTouchHelper进行拖拽排序功能】
版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
记录使用ItemTouchHelper对Recyclerview进行拖拽排序功能的实现。
效果图

代码分析
ItemTouchHelper是一个工具类,可实现侧滑删除和拖拽移动,使用这个工具类需要RecyclerView和Callback。同时根据需要重写onMove和onSwiped方法。
使用步骤
一、项目组织结构图


注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖
二、导入步骤
(1)在build.gradle中引用recyclerview【版本号和appcompat保持一致】
apply plugin: 'com.android.application'
android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.why.project.recyclerviewitemtouchhelperdemo"
        minSdkVersion 16
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    //RecyclerView
    compile "com.android.support:recyclerview-v7:27.1.1"
}
(2)在AndroidManifest.xml中声明震动权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.why.project.recyclerviewitemtouchhelperdemo"> <!-- ======================RecyclerView拖拽震动效果====================== -->
<uses-permission android:name="android.permission.VIBRATE" /> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application> </manifest>
(3)在项目中实现Recyclerview基本数据展现
1、创建Bean类
package com.why.project.recyclerviewitemtouchhelperdemo.bean; /**
* Created by HaiyuKing
* Used 列表项的bean类
*/ public class ChannelBean {
private String channelId;//频道id值
private String channelName;//频道名称 public String getChannelId() {
return channelId;
} public void setChannelId(String channelId) {
this.channelId = channelId;
} public String getChannelName() {
return channelName;
} public void setChannelName(String channelName) {
this.channelName = channelName;
}
}
ChannelBean.java
2、创建Adapter以及item的布局文件
package com.why.project.recyclerviewitemtouchhelperdemo.adapter; import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView; import com.why.project.recyclerviewitemtouchhelperdemo.R;
import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean; import java.util.ArrayList; /**
* Created by HaiyuKing
* Used 频道列表适配器
*/ public class ChannelAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
/**上下文*/
private Context myContext;
/**频道集合*/
private ArrayList<ChannelBean> listitemList; /**
* 构造函数
*/
public ChannelAdapter(Context context, ArrayList<ChannelBean> itemlist) {
myContext = context;
listitemList = itemlist;
} /**
* 获取总的条目数
*/
@Override
public int getItemCount() {
return listitemList.size();
} /**
* 创建ViewHolder
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(myContext).inflate(R.layout.channel_list_item, parent, false);
ItemViewHolder itemViewHolder = new ItemViewHolder(view);
return itemViewHolder;
} /**
* 声明grid列表项ViewHolder*/
static class ItemViewHolder extends RecyclerView.ViewHolder
{
public ItemViewHolder(View view)
{
super(view); listItemLayout = (LinearLayout) view.findViewById(R.id.listitem_layout);
mChannelName = (TextView) view.findViewById(R.id.tv_channelName);
} LinearLayout listItemLayout;
TextView mChannelName;
} /**
* 将数据绑定至ViewHolder
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int index) { //判断属于列表项还是上拉加载区域
if(viewHolder instanceof ItemViewHolder){
ChannelBean channelBean = listitemList.get(index);
final ItemViewHolder itemViewHold = ((ItemViewHolder)viewHolder); itemViewHold.mChannelName.setText(channelBean.getChannelName()); //如果设置了回调,则设置点击事件
if (mOnItemClickLitener != null)
{
itemViewHold.listItemLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了
mOnItemClickLitener.onItemClick(itemViewHold.listItemLayout, position);
}
});
//长按事件
itemViewHold.listItemLayout.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
int position = itemViewHold.getLayoutPosition();//在增加数据或者减少数据时候,position和index就不一样了
mOnItemClickLitener.onItemLongClick(itemViewHold.listItemLayout, position);
return false;
}
});
} }
} /**
* 添加Item--用于动画的展现*/
public void addItem(int position,ChannelBean listitemBean) {
listitemList.add(position,listitemBean);
notifyItemInserted(position);
}
/**
* 删除Item--用于动画的展现*/
public void removeItem(int position) {
listitemList.remove(position);
notifyItemRemoved(position);
} /*=====================添加OnItemClickListener回调================================*/
public interface OnItemClickLitener
{
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
} private OnItemClickLitener mOnItemClickLitener; public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener)
{
this.mOnItemClickLitener = mOnItemClickLitener;
}
}
ChannelAdapter.java
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/listitem_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:layout_margin="10dp"
android:background="#c5c5c5"> <TextView
android:id="@+id/tv_channelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="频道名称"
android:textSize="18sp"
android:layout_gravity="center"/> </LinearLayout>
channel_list_item.xml
3、在Activity布局文件中引用Recyclerview控件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:cacheColorHint="#00000000"
android:divider="@null"
android:listSelector="#00000000"
android:scrollbars="none"
/> </RelativeLayout>
4、在Activity类中初始化recyclerview数据
package com.why.project.recyclerviewitemtouchhelperdemo; import android.app.Service;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.View;
import android.widget.Toast; import com.why.project.recyclerviewitemtouchhelperdemo.adapter.ChannelAdapter;
import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean; import java.util.ArrayList;
import java.util.Collections; public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView;
private ArrayList<ChannelBean> mChannelBeanArrayList;
private ChannelAdapter mChannelAdapter; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initDatas();
initEvents(); } private void initViews() {
mRecyclerView = findViewById(R.id.recycler_view);
} private void initDatas() {
//初始化集合
mChannelBeanArrayList = new ArrayList<ChannelBean>();
for(int i=0; i<10;i++){
ChannelBean channelBean = new ChannelBean();
channelBean.setChannelId("123"+i);
channelBean.setChannelName("频道"+i); mChannelBeanArrayList.add(channelBean);
} //设置布局管理器
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
mRecyclerView.setLayoutManager(gridLayoutManager); //设置适配器
if(mChannelAdapter == null){
//设置适配器
mChannelAdapter = new ChannelAdapter(this, mChannelBeanArrayList);
mRecyclerView.setAdapter(mChannelAdapter);
//添加分割线
//设置添加删除动画
//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
mRecyclerView.setSelected(true);
}else{
mChannelAdapter.notifyDataSetChanged();
}
} private void initEvents() {
//列表适配器的点击监听事件
mChannelAdapter.setOnItemClickLitener(new ChannelAdapter.OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, mChannelBeanArrayList.get(position).getChannelName(), Toast.LENGTH_SHORT).show();
} @Override
public void onItemLongClick(View view, int position) {
Toast.makeText(MainActivity.this, "长按", Toast.LENGTH_SHORT).show();
}
});
}
}
三、使用方法
在基本的Recyclerview基础上添加ItemTouchHelper【注意,紫色标记的是用来演示用的,可以去掉】
package com.why.project.recyclerviewitemtouchhelperdemo; import android.app.Service;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.View;
import android.widget.Toast; import com.why.project.recyclerviewitemtouchhelperdemo.adapter.ChannelAdapter;
import com.why.project.recyclerviewitemtouchhelperdemo.bean.ChannelBean; import java.util.ArrayList;
import java.util.Collections; public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView;
private ArrayList<ChannelBean> mChannelBeanArrayList;
private ChannelAdapter mChannelAdapter; /**拖拽功能*/
private ItemTouchHelper itemTouchHelper;
private int currentPagePosition = -1;//当前拖拽的item的原始位置,从0开始【长按时赋值】,用来和currentPageNewPosition对比进行判断是否执行排序接口
private int currentPageNewPosition = -1;//当前item拖拽后的位置,从0开始
private boolean newOrder = false;//标记是否拖拽排序过,默认是false @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initDatas();
initEvents(); } private void initViews() {
mRecyclerView = findViewById(R.id.recycler_view);
} private void initDatas() {
//初始化集合
mChannelBeanArrayList = new ArrayList<ChannelBean>();
for(int i=0; i<10;i++){
ChannelBean channelBean = new ChannelBean();
channelBean.setChannelId("123"+i);
channelBean.setChannelName("频道"+i); mChannelBeanArrayList.add(channelBean);
} //设置布局管理器
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,3);
mRecyclerView.setLayoutManager(gridLayoutManager); //设置适配器
if(mChannelAdapter == null){
//设置适配器
mChannelAdapter = new ChannelAdapter(this, mChannelBeanArrayList);
mRecyclerView.setAdapter(mChannelAdapter);
//添加分割线
//设置添加删除动画
//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
mRecyclerView.setSelected(true);
}else{
mChannelAdapter.notifyDataSetChanged();
} initItemTouchHelper();
} private void initItemTouchHelper() {
//拖拽
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback(){ //开启长按拖拽功能,默认为true【暂时用不到】
//如果需要我们自定义拖拽和滑动,可以设置为false,然后调用itemTouchHelper.startDrag(ViewHolder)方法来开启!
@Override
public boolean isLongPressDragEnabled() {
return true;
} //开始滑动功能,默认为true【暂时用不到】
//如果需要我们自定义拖拽和滑动,可以设置为false,然后调用itemTouchHelper.startSwipe(ViewHolder)方法来开启!
@Override
public boolean isItemViewSwipeEnabled() {
return true;
} /*用于设置是否处理拖拽事件和滑动事件,以及拖拽和滑动操作的方向
比如如果是列表类型的RecyclerView,拖拽只有UP、DOWN两个方向
而如果是网格类型的则有UP、DOWN、LEFT、RIGHT四个方向
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;//dragFlags 是拖拽标志
int swipeFlags = 0;//swipeFlags是侧滑标志,我们把swipeFlags 都设置为0,表示不处理滑动操作
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
swipeFlags = 0;
} else {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
swipeFlags = 0;
}
Log.w("ItemTouchHelper","{getMovementFlags}dragFlags="+dragFlags+";swipeFlags="+swipeFlags);
return makeMovementFlags(dragFlags, swipeFlags);
} /*如果我们设置了非0的dragFlags ,那么当我们长按item的时候就会进入拖拽并在拖拽过程中不断回调onMove()方法
我们就在这个方法里获取当前拖拽的item和已经被拖拽到所处位置的item的ViewHolder,
有了这2个ViewHolder,我们就可以交换他们的数据集并调用Adapter的notifyItemMoved方法来刷新item*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position
int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position
Log.w("ItemTouchHelper","{onMove}fromPosition="+fromPosition+";toPosition="+toPosition);
//这里可以添加判断,实现某一项不可交换
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mChannelBeanArrayList, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mChannelBeanArrayList, i, i - 1);
}
}
mChannelAdapter.notifyItemMoved(fromPosition, toPosition); return true;
} /*同理如果我们设置了非0的swipeFlags,我们在侧滑item的时候就会回调onSwiped的方法,我们不处理这个事件,空着就行了。*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { }
//我们希望拖拽的Item在拖拽的过程中发生震动或者颜色变深,这样就需要继续重写下面两个方法
//当长按选中item的时候(拖拽开始的时候)调用
//ACTION_STATE_IDLE:闲置状态
//ACTION_STATE_SWIPE:滑动状态
//ACTION_STATE_DRAG:拖拽状态
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
Log.w("ItemTouchHelper","{onSelectedChanged}actionState="+actionState);
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
//获取系统震动服务
Vibrator vib = (Vibrator) MainActivity.this.getSystemService(Service.VIBRATOR_SERVICE);
//震动70毫秒
vib.vibrate(70);
viewHolder.itemView.setPressed(true);
viewHolder.itemView.setBackgroundColor(Color.parseColor("#ff0000"));//演示拖拽的时候item背景颜色加深(实际情况中去掉)
}
super.onSelectedChanged(viewHolder, actionState);
} //当手指松开的时候(拖拽或滑动完成的时候)调用,这时候我们可以将item恢复为原来的状态(相对于背景颜色加深来说的)
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
Log.w("ItemTouchHelper","{clearView}viewHolder.getAdapterPosition="+viewHolder.getAdapterPosition());
viewHolder.itemView.setPressed(false);
currentPageNewPosition = viewHolder.getAdapterPosition();
Log.w("ItemTouchHelper","{clearView}currentPagePosition="+currentPagePosition);
Log.w("ItemTouchHelper","{clearView}currentPageNewPosition="+currentPageNewPosition);
if(!(currentPagePosition == currentPageNewPosition)){
newOrder = true;
//执行其他方法,比如设置拖拽后的item为选中状态
} viewHolder.itemView.setBackgroundColor(Color.parseColor("#c5c5c5"));//演示拖拽的完毕后item背景颜色恢复原样(实际情况中去掉)
mChannelAdapter.notifyDataSetChanged();//解决重叠问题
}
});
//设置是否可以排序
itemTouchHelper.attachToRecyclerView(mRecyclerView);
} private void initEvents() {
//列表适配器的点击监听事件
mChannelAdapter.setOnItemClickLitener(new ChannelAdapter.OnItemClickLitener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this, mChannelBeanArrayList.get(position).getChannelName(), Toast.LENGTH_SHORT).show();
} @Override
public void onItemLongClick(View view, int position) {
currentPagePosition = position;//拖拽用到的
Toast.makeText(MainActivity.this, "长按", Toast.LENGTH_SHORT).show();
}
});
}
}
混淆配置
无
参考资料
Android使用ItemTouchHelper打造可拖拽的RecyclerView
RecyclerView进阶:使用ItemTouchHelper实现拖拽和侧滑删除
RecyclerView爱恨情仇之ItemTouchHelper
项目demo下载地址
https://github.com/haiyuKing/RecyclerViewItemTouchHelperDemo
RecyclerViewItemTouchHelperDemo【使用ItemTouchHelper进行拖拽排序功能】的更多相关文章
- php接口实现拖拽排序功能
		
列表拖拽排序是一个很常见的功能,但是后端接口如何处理却是一个令人纠结的问题 如何实现才能达到效率最高呢 先分析一个场景,假如有一个页面有十条数据,所谓的拖拽就是在这十条数据来来回回的拖,但是每次拖动都 ...
 - vue列表拖拽排序功能实现
		
1.实现目标:目标是输入一个数组,生成一个列表:通过拖拽排序,拖拽结束后输出一个经过排序的数组. 2.实现思路: 2.1是使用HTML5的drag功能来实现,每次拖拽时直接操作Dom节点排序,拖拽结束 ...
 - vue el-transfer新增拖拽排序功能---sortablejs插件
		
<template> <!-- target-order="unshift"必须设置,如果不设置的话后台穿的value值得顺序会被data重置 - --> ...
 - ListBox实现拖拽排序功能
		
1.拖拽需要实现的事件包括: PreviewMouseLeftButtonDown LBoxSort_OnDrop 具体实现如下: private void LBoxSort_OnPreviewMou ...
 - vue2.0 不引用第三方包的情况下实现嵌套对象的拖拽排序功能
		
先上一张效果图,然后再上代码(由于只做效果,未做数据相关的处理:实际处理数据时不修改 dom 元素,只是利用 dom 元素传递数据,然后需改数据,靠数据驱动效果) <div :id=" ...
 - ztree使用系列四(ztree实现同级拖拽排序并将排序结果保存数据库)
		
ztree这个系列的最后一篇,也是ztree功能强大的体现之中的一个--排序功能. ztree能够实现全部节点之间任意的拖拽排序功能.我这里依据须要实现了仅仅同意同级之间任意拖拽排序,事实上原理都一样 ...
 - Sortable拖拽排序插件数据筛选
		
后台有拖拽排序功能,然而前段在开发的时候,一整页的数据都发给后端了. 于是查看前端代码,想到了如下解决办法,即先把排序前的保存,然后对比排序后的,有差异的才发回给后端. var new_ids_ord ...
 - easyui树节点拖拽排序的存储过程
		
easyui树的拖拽排序功能 easyui树中有拖拽功能 树结构如下: 一个行政区域对应一个单位,一个单位对应多个部门,每个部门下有相关人员,功能要求: (1)行政区域没有子节点,点击text加载部门 ...
 - vue中基于sortablejs与el-upload实现文件上传后拖拽排序
		
今天做冒烟测试的时候发现商品发布有一个拖拽图片排序功能没做,赶紧加上 之前别的同事基于 vuedraggable 实现过这个功能,我这里自己深度封装了 el-upload ,用这种方式改动很大,而且感 ...
 
随机推荐
- HTML5 & MUI 界面样式
			
垂直居中+自动换行 样式效果如下所示,当文字没有超出一行时,显示如“备注信息”,当文字超出一行时,显示如“维修地点” HTML代码如下: <div class="mui-input-r ...
 - Python数据运算
			
身份运算 is is是判断两个标识符是不是引用自一个对象 x is y, 如果id(x)等于id(y), is 返回结果1 is not is not 是判断两个标识符是不是引用自不同对象 x is ...
 - AutoIT 测试GUI工具
			
今天听到同事提到AutoIT,可以用来测试GUI窗口.了解一下这个工具. 以下内容引自: http://www.jb51.net/article/14870.htm (此url非原出处,该博主未注明原 ...
 - 如何添加“在这里打开PowerShell”到Windows中的上下文菜单
			
It was only a matter of time, right? Due to my recent infatuation passionate love affair with PowerS ...
 - Ubuntu配置tomcat9
			
buntu 安装jdk:[链接] Ubuntu安装eclipse:[链接] Ubuntu下安装MySQL与mysql workbench:[链接] Ubuntu配置tomcat9:[链接] Ubunt ...
 - Hadoop2.41的HA的配置与启动
			
我配置HA机制创建了7台虚拟机 1.修改Linux主机名2.修改IP3.修改主机名和IP的映射关系 ######注意######如果你们公司是租用的服务器或是使用的云主机(如华为云主机.阿里云主机等) ...
 - 从壹开始前后端分离【 .NETCore2.1 +Vue 2 +AOP+DI】框架之一 || 前言
			
缘起 作为一个.Net攻城狮已经4年有余了,一直不温不火,正好近来项目不是很忙,闲得无聊,搞一搞新技术,一方面是打发无聊的时间,一方面也是督促自己该学习辣!身边的大神都转行的转行,加薪的加薪,本人比较 ...
 - 第一章.java&golang的区别之:闭包
			
对于golang一直存有觊觎之心,但一直苦于没有下定决心去学习研究,最近开始接触golang.就我个人来说,学习golang的原动力是因为想要站在java语言之外来审视java和其它语言的区别,再就是 ...
 - ArrayList 和 LinkedList 源码分析
			
List 表示的就是线性表,是具有相同特性的数据元素的有限序列.它主要有两种存储结构,顺序存储和链式存储,分别对应着 ArrayList 和 LinkedList 的实现,接下来以 jdk7 代码为例 ...
 - 实时语音趣味变声,大叔变声“妙音娘子”Get一下
			
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯游戏云 发表于云+社区专栏 游戏社交化是近年来游戏行业发展的重要趋势,如何提高游戏的社交属性已成为各大游戏厂商游戏策划的重要组成部 ...