Eoe客户端源码分析---SlidingMenu的使用
来源:互联网 发布:视频点播数据分析报告 编辑:程序博客网 时间:2024/05/09 11:03
Eoe客户端源码分析及代码注释
使用滑动菜单SlidingMenu,单击滑动菜单的不同选项,可以通过ViewPager和PagerIndicator显示对应的数据内容。
0 BaseSlidingFragmentActivity.java
主要函数:
(1)showMenu()
/** * Opens the menu and shows the menu view.*/
public void showMenu() {
showMenu(true);
}
(2)showContent()
/**Closes the menu and shows the above view. */
public void showContent() {
showContent(true);
}
(3)toggle()
/**Toggle the SlidingMenu. If it is open, it will be closed, and viceversa.*/
public void toggle() {
toggle(true);
}
/**
* Toggle the SlidingMenu. If it is open, itwill be closed, and vice versa.
*
* @param animate true to animate thetransition, false to ignore animation
*/
public void toggle(booleananimate) {
if (isMenuShowing()) {
showContent(animate);
} else {
showMenu(animate);
}
}
(4)
//设置SlidingMenu使用的布局
publicvoid setBehindContentView(int id){
setBehindContentView(getLayoutInflater().inflate(id,null));
}
public void setBehindContentView(View v) {
setBehindContentView(v, new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT));
}
public void setBehindContentView(View v, LayoutParams params) {
mHelper.setBehindContentView(v, params);
}
//获取与该Acitivity相关的SlidingMenu对象
public SlidingMenu getSlidingMenu() {
returnmHelper.getSlidingMenu();
}
1. behind_sldingmenu.xml 滑动菜单的部分页面布局文件
滑动菜单主要由一个标题布局(@layout/behinf_title)、菜单选项列表布局和两个自定义图形按钮等控件元素组成
<FrameLayoutxmlns: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:background="#dadada"
android:orientation="vertical">
<!--sliding menu layout -->
<includelayout="@layout/behind_title"/>
<ListView
android:id="@+id/behind_list_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:layout_marginBottom="@dimen/list_margin_height"
android:layout_marginTop="@dimen/title_height"
android:divider="@drawable/dis_behind_side"
android:listSelector="#0fff"
android:cacheColorHint="#0000">
</ListView>
</FrameLayout>
2. 初始化滑动菜单
private SlidingMenu sm;
// [start]初始化函数
private void initSlidingMenu() {
//设置滑动菜单的布局文件
setBehindContentView(R.layout.behind_slidingmenu);
// 获取滑动菜单对象并设置外观属性customize the SlidingMenu
sm = getSlidingMenu();
sm.setShadowWidthRes(R.dimen.shadow_width);
sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);
// sm.setFadeDegree(0.35f);
sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
sm.setShadowDrawable(R.drawable.slidingmenu_shadow);
//sm.setShadowWidth(20);
sm.setBehindScrollScale(0);
}
3. 显示菜单:
(1)单击主界面左上角的LinearLayout控件
// fromabove_title.xml -- > pop up slidingmenu (showMenu() )
llGoHome = (LinearLayout) findViewById(R.id.Linear_above_toHome);
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.Linear_above_toHome:
showMenu();
break;
}
}
(2)单击手机的菜单选项
@Override
public boolean onKeyDown(int keyCode, KeyEventevent) {
else if (keyCode == KeyEvent.KEYCODE_MENU) {
if (sm.isMenuShowing()) {
toggle();
} else {
showMenu();
}
}
}
4. 设置填充滑动菜单中列表项数据的适配器
4.1 behind_list_show.xml (每一个列表项的布局)
每一个列表项(子菜单选项)由一个ImageView(子菜单图标)和TextView(子菜单名称)组成
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/behind_list_height"
android:orientation="horizontal"
android:background="@drawable/back_behind_listitem_style">
<ImageView
android:id="@+id/imageview_behind_icon"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/dis_menu_blog"
android:scaleType="fitCenter"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"/>
<TextView
android:id="@+id/textview_behind_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="#666"
android:textSize="@dimen/behind_list_text_size"/>
</LinearLayout>
4.2定义getData函数获取填充列表项的数据List
private List<Map<String, Object>> getData() {
List<Map<String, Object>>list = new ArrayList<Map<String, Object>>();
//社区精选
Map<String, Object> map = new HashMap<String,Object>();
map.put(LIST_TEXT, getResources().getString(R.string.menuGood));
map.put(LIST_IMAGEVIEW, R.drawable.dis_menu_handpick);
list.add(map);
//新闻资讯
map = new HashMap<String, Object>();
map.put(LIST_TEXT, getResources().getString(R.string.menuNews));
map.put(LIST_IMAGEVIEW, R.drawable.dis_menu_news);
list.add(map);
//学习教程
map = new HashMap<String, Object>();
map.put(LIST_TEXT, getResources().getString(R.string.menuStudio));
map.put(LIST_IMAGEVIEW, R.drawable.dis_menu_studio);
list.add(map);
//社区博客
map = new HashMap<String, Object>();
map.put(LIST_TEXT, getResources().getString(R.string.menuBlog));
map.put(LIST_IMAGEVIEW, R.drawable.dis_menu_blog);
list.add(map);
return list;
}
4.3定义列表项数据填充适配器
SimpleAdapter lvAdapter = new SimpleAdapter(this, getData(),
R.layout.behind_list_show,new String[]{LIST_TEXT,
LIST_IMAGEVIEW},
new int[]{R.id.textview_behind_title,
R.id.imageview_behind_icon}) {
@Override
publicView getView(int position, View convertView, ViewGroupparent) {
View view = super.getView(position,convertView, parent);
//如果是当前选中的子菜单项
if (position ==mTag) {
//设置新的背景图片 标识该菜单选项被选中
view.setBackgroundResource(R.drawable.back_behind_list);
lvTitle.setTag(view);//绑定当前选中的子菜单选项到lvTitle上
} else {
view.setBackgroundColor(Color.TRANSPARENT);
}
return view;
}
};
5. 单击滑动菜单中的列表项 启动不同的ViewPager
(1)NavigationModel 类的定义
package cn.eoe.app.entity;
public class NavigationModel {
private Stringname;
//作为唯一标识符 newsblog wiki方便于每个页面请求相对应的地址
private Stringtags;
public NavigationModel(String name1,String tags1){
this.name = name1;
this.tags = tags1;
}
}
(2)创建NavigationModel 对象,并添加四个子菜单对应的数据(name,tag)
private List<NavigationModel> navs;
private void initNav() {
navs = new ArrayList<NavigationModel>();
NavigationModel nav1 =newNavigationModel(getResources().getString(
R.string.menuGood),"");
NavigationModel nav2 =newNavigationModel(getResources().getString(
R.string.menuNews), Constants.TAGS.NEWS_TAG);
NavigationModel nav3 =newNavigationModel(getResources().getString(
R.string.menuStudio),Constants.TAGS.WIKI_TAG);
NavigationModel nav4 =newNavigationModel(getResources().getString(
R.string.menuBlog),Constants.TAGS.BLOG_TAG);
Collections.addAll(navs, nav1, nav2, nav3, nav4);
}
(3)初始化列表项数据
MainActivity.java
initialListView(){}
lvAdapter = new SimpleAdapter(this, getData(),
R.layout.behind_list_show,new String[]{LIST_TEXT,
LIST_IMAGEVIEW},
new int[]{R.id.textview_behind_title,
R.id.imageview_behind_icon});
lvTitle.setAdapter(lvAdapter);
(4)列表项单击事件监听函数
lvTitle.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent,View view,
int position,long id) {
NavigationModel navModel = navs.get(position);
mAboveTitle.setText(navModel.getName());
current_page = navModel.getTags();
if (lvTitle.getTag() !=null) {
if (lvTitle.getTag() == view) {
//如果本次单击的子菜单选项(view)和上一次选择的子菜单选项(lvTitle.getTag())相同,则直接显示当前子菜单项对应的内容
MainActivity.this.showContent();
return;
}
//若果不相同,重新将原来菜单选项的背景色改为透明
((View) lvTitle.getTag())
.setBackgroundColor(Color.TRANSPARENT);
}
lvTitle.setTag(view);//重新绑定新的子菜单选项到lvTitle上
//设置新的背景图片 标识该菜单选项被选中
view.setBackgroundResource(R.drawable.back_behind_list);
imgQuery.setVisibility(View.VISIBLE);
//根据选择的不同子菜单选项执行不同的异步任务,显示对应的数据内容
switch (position) {
case 0:
imgQuery.setVisibility(View.GONE);
newMyTask().execute(topDao);
break;
case 1:
newMyTask().execute(newsDao);
break;
case 2:
newMyTask().execute(wikiDao);
break;
case 3:
new MyTask().execute(blogsDao);
break;
}
}
});
6. 分离出来的模板例子
在MainActivity中显示选中的滑动菜单选项的名称
(1) main.xml (MainActivity)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/sso_topbar" > <ImageButton android:layout_marginLeft="5dip" android:layout_centerVertical="true" android:id="@+id/imgbtn_top_left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_flipper_head_flip"/> <TextView android:layout_toRightOf="@id/imgbtn_top_left" android:id="@+id/tv_top_center" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/tv_top_center" android:textColor="@color/whilte" android:layout_centerVertical="true" android:layout_marginLeft="25dip" /> <ImageButton android:id="@+id/imgbtn_top_right" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/feed_refresh_arrow_pressed" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginRight="5dip"/> </RelativeLayout> <TextView android:id="@+id/show_submenu_content" android:layout_width="wrap_content" android:layout_height="wrap_content" /></LinearLayout>
(2)fragment_left_bottom.xml (滑动菜单的布局文件)
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/renren_news_first_image_bg" android:orientation="vertical" > <!-- 顶部头像,姓名,标签 --> <include layout="@layout/left_bottom_top" /> <ScrollView android:layout_width="fill_parent" android:layout_height="wrap_content" > <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" > <!-- 常用 --> <TextView android:id="@+id/left_tv_commom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="3dip" android:paddingLeft="20dip" android:paddingTop="3dip" android:text="@string/left_bottom_commom" android:textColor="@color/whilte" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_divider_new"/> <!-- 常用列表 --><!-- 常用列表与更多列表使用同一个item布局 自定义适配器 --> <com.pps.myrenren.custom.MyListView android:id="@+id/listview_common" android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@drawable/v5_0_1_divider_line_new"/> <TextView android:id="@+id/left_tv_more" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="3dip" android:paddingLeft="20dip" android:paddingTop="3dip" android:text="@string/left_bottom_more" android:textColor="@color/whilte" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_divider_new"/> <!-- 更多列表 --> <com.pps.myrenren.custom.MyListView android:id="@+id/listview_more" android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@drawable/v5_0_1_divider_line_new"/> <!-- <TextView android:id="@+id/left_tv_recommend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="3dip" android:paddingLeft="20dip" android:paddingTop="3dip" android:text="@string/left_bottom_recommend" android:textColor="@color/whilte" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_divider_line_new"/> <ListView android:id="@+id/listview_recommend" android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@drawable/v5_0_1_divider_new"/> <TextView android:id="@+id/left_tv_app" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="3dip" android:paddingLeft="20dip" android:paddingTop="3dip" android:text="@string/left_bottom_app" android:textColor="@color/whilte" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_divider_line_new"/> <ListView android:id="@+id/listview_app" android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@drawable/v5_0_1_divider_new"/> --> <TextView android:id="@+id/left_tv_setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="3dip" android:paddingLeft="20dip" android:paddingTop="3dip" android:text="@string/left_bottom_setting" android:textColor="@color/whilte" /> <ImageView android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/v5_0_1_divider_new"/> <com.pps.myrenren.custom.MyListView android:id="@+id/listview_setting" android:layout_width="fill_parent" android:layout_height="wrap_content" android:cacheColorHint="#00000000" android:divider="@drawable/v5_0_1_divider_line_new"/> </LinearLayout> </ScrollView></LinearLayout>(3) menu_frame.xml 滑动菜单布局文件(装载Fragment的FrameLayout容器)
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/menu_frame" android:layout_width="match_parent" android:layout_height="match_parent" />(4) MainActivity.java
Key:在Activtiy中实现在(5)中[LeftBottomFragment.java]定义的回调方法
package com.pps.myrenren.activity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.view.Window;import android.widget.ImageButton;import android.widget.TextView;import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;import com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity;public class MainActivity extends SlidingFragmentActivity implements LeftBottomFragment.SLMenuListOnItemClickListener{private ImageButton imgbtn_top_left;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);this.requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.main);imgbtn_top_left=(ImageButton)this.findViewById(R.id.imgbtn_top_left);imgbtn_top_left.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {/*Tip1:Toggle the SlidingMenu. If it is open, it will be closed, and vice versa.*/toggle();}});//Initial Sliding MenuinitSlidingMenu(savedInstanceState);}//0628@Overridepublic void selectItem(int position,String title){//toggle();showContent();//hide the menu TextView tv = (TextView)findViewById(R.id.show_submenu_content);tv.setText(title);}/** * Initial Sliding Menu */private void initSlidingMenu(Bundle savedInstanceState) {// 设置滑动菜单的视图setBehindContentView(R.layout.menu_frame);getSupportFragmentManager().beginTransaction().replace(R.id.menu_frame, new LeftBottomFragment()).commit();// 实例化滑动菜单对象SlidingMenu sm = getSlidingMenu();// 设置滑动阴影的宽度sm.setShadowWidthRes(R.dimen.shadow_width);// 设置滑动阴影的图像资源sm.setShadowDrawable(R.drawable.shadow);// 设置滑动菜单视图的宽度sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);// 设置渐入渐出效果的值sm.setFadeDegree(0.35f);// 设置触摸屏幕的模式sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);}}
(5)LeftBottomFragment.java
Key:在菜单Fragment中定义回调函数 将Fragment的信息传递到Activity
package com.pps.myrenren.activity;import java.util.ArrayList;import java.util.List;import android.annotation.SuppressLint;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import com.pps.myrenren.adapter.CommonOrMoreAdapter;import com.pps.myrenren.adapter.SettingAdapter;import com.pps.myrenren.model.ItemComOrMoreModel;import com.pps.myrenren.model.ItemSettingModel;public class LeftBottomFragment extends Fragment{private View mView;private Context mContext;private ListView listview_common;private ListView listview_more;private ListView listview_setting;private List<ItemComOrMoreModel> commonModels; //常用列表的Item集合private List<ItemComOrMoreModel> moreModels; //更多列表的item集合private List<ItemSettingModel> settingModels; //设置列表的item集合private SLMenuListOnItemClickListener mCallbacks;//0628//Key:public void onAttach(Activity activity){ super.onAttach(activity); //如果该Acitivity没有实现Callbacks接口 if(!(activity instanceof SLMenuListOnItemClickListener)){ try { throw new Exception("LeftBottomFragment所在的Activity" + "必须实现Callbacks接口"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //定义回调函数的接口对象的实例化--- //把该Activity当成Callbacks对象 mCallbacks = (SLMenuListOnItemClickListener) activity; } public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {if (null == mView) {mView = inflater.inflate(R.layout.fragment_left_bottom, container,false);initView();initValidata();bindData();initialClickListener();//}return mView;}/** * 初始化界面元素 */private void initView() {//---mView.findViewById() not .findViewById()listview_common = (ListView) mView.findViewById(R.id.listview_common);listview_more = (ListView) mView.findViewById(R.id.listview_more);listview_setting = (ListView) mView.findViewById(R.id.listview_setting);}/** * 初始化变量 */private void initValidata() {mContext = mView.getContext();commonModels=new ArrayList<ItemComOrMoreModel>();moreModels=new ArrayList<ItemComOrMoreModel>();settingModels=new ArrayList<ItemSettingModel>();//1:进行构造常用列表中的数据,图标,名称,数量Integer[] common_icon_id = new Integer[] {R.drawable.v5_2_1_desktop_list_newsfeed,R.drawable.v5_2_1_desktop_list_message,R.drawable.v5_2_1_desktop_list_chat,R.drawable.v5_2_1_desktop_list_friends,R.drawable.v5_2_1_desktop_list_search,R.drawable.v5_9_3_desktop_list_barcode };String[] arrays_commom=mContext.getResources().getStringArray(R.array.arrays_commom);int[] common_number=new int[]{0,1,2,3,4,1};for(int i=0;i<common_icon_id.length;i++){ItemComOrMoreModel commcon=new ItemComOrMoreModel(common_icon_id[i], arrays_commom[i], common_number[i]);commonModels.add(commcon);}//2:进行构造更多列表中的数据,图标,名称,数量Integer[] more_icon_id=new Integer[]{R.drawable.v5_2_1_desktop_list_location,R.drawable.v5_2_1_desktop_list_page,R.drawable.v5_2_0_desktop_list_hot,R.drawable.v5_2_1_desktop_list_apps_center};String[] arrays_more=mContext.getResources().getStringArray(R.array.arrays_more);int[] more_number=new int[]{0,0,0,0};for(int i=0;i<more_icon_id.length;i++){ItemComOrMoreModel more=new ItemComOrMoreModel(more_icon_id[i],arrays_more[i],more_number[i]); moreModels.add(more);}//3:进行构造设置列表中的数据,图标,名称Integer[] setting_icon_id=new Integer[]{R.drawable.v_5_8day_mode_unselected,R.drawable.v5_2_1_desktop_list_settings,R.drawable.v5_2_1_desktop_list_log_out};String[] arrays_setting=mContext.getResources().getStringArray(R.array.arrays_setting);for(int i=0;i<setting_icon_id.length;i++){ItemSettingModel setting=new ItemSettingModel(setting_icon_id[i],arrays_setting[i]);settingModels.add(setting);}}/** * 绑定数据 */private void bindData() { //创建适配器并且进行绑定数据到listview中listview_common.setAdapter(new CommonOrMoreAdapter(mContext, commonModels));listview_more.setAdapter(new CommonOrMoreAdapter(mContext, moreModels));listview_setting.setAdapter(new SettingAdapter(mContext, settingModels));}//0628:点击滑动菜单子选项的回调接口public interface SLMenuListOnItemClickListener{ public void selectItem(int position,String title); }/*菜单选项单击响应事件的监听函数*/public void initialClickListener(){//listview_common.setOnItemClickListener(new OnItemClickListener(){@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {//激发mCallbacks的selectItem方法 mCallbacks.selectItem(position, commonModels.get(position).getName());}});listview_more.setOnItemClickListener(new OnItemClickListener(){@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {mCallbacks.selectItem(position, moreModels.get(position).getName());}});listview_setting.setOnItemClickListener(new OnItemClickListener(){@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position,long id) {// TODO Auto-generated method stubmCallbacks.selectItem(position, settingModels.get(position).getName());}});}}(6)其它文件 省略
- Eoe客户端源码分析---SlidingMenu的使用
- Eoe客户端源码分析---ViewPager、 PageAdapter和PageIndicator 的使用
- eoe android客户端源码剖析(二)侧滑菜单SlidingMenu
- 【android】SlidingMenu源码分析
- SlidingMenu源码分析
- EOE客户端阅读和项目的问题
- 如何使用Github参与开源项目(以eoe的Android客户端为例)
- eoe android客户端源码剖析(一)动画启动界面
- SlidingMenu源码分析(二)
- android eoe客户端
- eoe:开篇-记录EOE源码学习过程
- Slidingmenu源码分析一 (初步了解)
- android slidingmenu的使用
- SlidingMenu的使用例子
- slidingMenu的使用
- SlidingMenu的使用
- slidingMenu的使用教程
- SlidingMenu的使用
- 网页编程.笔记6:跑马灯
- 百度权重是什么?百度算法怎么算?
- Android Training - 使用IntentService执行任务(Lesson 2 - 发送任务给IntentService)
- google-breakpad介绍
- mysql 错误
- Eoe客户端源码分析---SlidingMenu的使用
- 初识JavaFX(二)
- 标准C程序设计(六)
- 收藏几个linux下监控使用的小工具
- Android Training - 使用IntentService执行任务(Lesson 3 - 回传任务状态给发送方)
- hive和hbase整合
- json2.js 使用
- HDU 1874 畅通工程续
- 游戏外挂基本原理及实现