楼主: 熊猫儿

【Android的一些重要知识6。。。。】

[复制链接]
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
291#
 楼主| 发表于 2016-9-14 10:34 | 只看该作者
Android Data Binding
作者:ashqal
1 引入
如何高效地实现以下界面?
有好几年findViewById实战经验的我,感觉并不难啊。一般会
* 1.先定义一个User的Model类,数据来自JSON解析;
* 2.创建一个xml,随后在xml中布局完所有View,对头像、标题、积分、登录按钮一个id;
* 3.在Activity中通过findViewById获取到头像ImageView、标题TextView、积分TextView、登录Button,然后给Button设置监听器,再根据登陆状态展示对应数据;
实现如下:
* User.java
public class User {    private String name;    private int score;    private int level;    private int avatar;    public String getName() { return name; }    public void setName(String name) { this.name = name; }    public int getScore() { return score; }    public void setScore(int score) { this.score = score; }    public int getLevel() { return level; }    public int getAvatar() { return avatar; }    public void setAvatar(int avatar) { this.avatar = avatar; }    public void setLevel(int level) { this.level = level; }    public static User newInstance() {        User user = new User();        user.setName("王大锤:" + (int)(Math.random() * 10));        user.setScore((int) (Math.random() * 999));        user.setLevel((int) (Math.random() * 77));        user.setAvatar(R.drawable.avatar);        return user;    }}


使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
292#
 楼主| 发表于 2016-9-14 10:34 | 只看该作者
  • activity_detail.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    androidrientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <View        android:background="@color/detail_background"        android:layout_width="match_parent"        android:layout_height="66dp">    </View>    <ImageView        android:id="@+id/detail_avatar"        android:layout_gravity="center"        android:src="@drawable/avatar"        android:layout_marginTop="-33dp"        android:layout_width="66dp"        android:layout_height="66dp" />    <TextView        android:id="@+id/detail_name"        android:textSize="17sp"        android:textColor="@color/textColorPrimary"        android:layout_marginTop="15dp"        android:layout_gravity="center"        android:text="王大锤"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <TextView        android:id="@+id/detail_desc"        android:layout_marginTop="15dp"        android:textSize="13sp"        android:layout_gravity="center"        android:text="积分:102 金币:0"        android:layout_width="wrap_content"        android:layout_height="wrap_content" />    <Button        android:id="@+id/detail_action_button"        android:layout_marginTop="15dp"        android:layout_gravity="center"        android:text="退出登陆"        android:textColor="@color/white"        android:background="@drawable/selector_g_button"        android:layout_width="220dp"        android:layout_height="wrap_content" /></LinearLayout>

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
293#
 楼主| 发表于 2016-9-14 10:36 | 只看该作者
  • DetailActivity
public class DetailActivity extends AppCompatActivity {    ImageView avatarIV;    TextView nameTV;    TextView descTV;    Button actionBtn;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_detail);        initView();        login();    }    private void login(){ fill(User.newInstance()); }    private void logout(){ fill(null); }    private void initView() {        avatarIV = (ImageView) findViewById(R.id.detail_avatar);        nameTV = (TextView) findViewById(R.id.detail_name);        descTV = (TextView) findViewById(R.id.detail_desc);        actionBtn = (Button) findViewById(R.id.detail_action_button);    }    private void fill(final User user){        final int visibility = user != null ? View.VISIBLE : View.GONE;        if (avatarIV != null){            avatarIV.setVisibility(visibility);            if (user != null)                avatarIV.setImageDrawable(ContextCompat.getDrawable(this,user.getAvatar()));        }        if (nameTV != null){            nameTV.setVisibility(visibility);            if (user != null)                nameTV.setText(user.getName());        }        if (descTV != null){            descTV.setVisibility(visibility);            if (user != null)                descTV.setText(String.format("积分:%d 等级:%d",user.getScore(),user.getLevel()));        }        if (actionBtn != null){            actionBtn.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    if (user == null) login();                    else logout();                }            });            actionBtn.setText(user == null ? "登录":"退出登录");        }    }}

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
294#
 楼主| 发表于 2016-9-14 10:37 | 只看该作者
2 去掉烦人的findViewById(View注入)
可以看到,在Activity中View的定义、find、判空占据了大量篇幅,我们需要更优雅的实现。
2.1 ButterKnife
你可能听说过Jake Wharton的ButterKnife,这个库只需要在定义View变量的时候通过注解传入对应id,随后在onCreate时调用ButterKnife.bind(this)即可完成view的注入,示例如下:
class ExampleActivity extends Activity {  @BindView(R.id.user) EditText username;  @BindView(R.id.pass) EditText password;  @Override public void onCreate(Bundle savedInstanceState{    super.onCreate(savedInstanceState);    setContentView(R.layout.simple_activity);    ButterKnife.bind(this);  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
2.2 Android Data Binding
如果使用了Android Data Binding,那么View的定义、find、判空这些都不用写了,如何做呢?
2.2.1 准备工作
首先,你需要满足一个条件:你的Android Plugin for Gradle版本必须等于或高于1.5.0-alpha1版本,这个版本位于根目录build.gradle中,示例如下:
buildscript {    repositories {        jcenter()    }    dependencies {        classpath 'com.android.tools.build:gradle:2.1.0-rc1'    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
接着,你必须告诉编译器开启Data Binding,一般位于app:build.gradle的android标签中,示例如下:
android {    compileSdkVersion 23    buildToolsVersion "23.0.2"    dataBinding {        enabled true    }    ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
2.2.2 修改layout.xml
以activity_detail.xml为例,原来的根节点为LinearLayout,如下所示:
http://lib.csdn.net/article/android/34439

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
295#
 楼主| 发表于 2016-9-14 10:37 | 只看该作者
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
我们拷一份activity_detail.xml,改为activity_detail2.xml,并且需要在外面wrap一层layout标签,修改后的activity_detail2.xml为:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <!-- LinearLayout为原布局根节点 -->    <LinearLayout        androidrientation="vertical" android:layout_width="match_parent"        android:layout_height="match_parent">        <View            android:background="@color/detail_background"            android:layout_width="match_parent"            android:layout_height="66dp">        </View>        ...    </LinearLayout></layout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
2.2.3 开始享受乐趣吧!
在上述操作完成后,编译器会自动为我们生成
com.asha.demo.databinding.ActivityDetail2Binding.java类,这个类的命令方式为:包名 + databinding + activity_detail2驼峰命名方式 + Binding.java。随后,使用这个activity_detail2的DetailActivity2.java的代码可以简化为:
public class DetailActivity2 extends AppCompatActivity {    ActivityDetail2Binding binding;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        binding = DataBindingUtil.setContentView(this,R.layout.activity_detail2);        login();    }    private void login(){ fill(User.newInstance()); }    private void logout(){ fill(null); }    private void fill(final User user){        final int visibility = user != null ? View.VISIBLE : View.GONE;        if (user != null){            binding.detailAvatar.setImageDrawable(ContextCompat.getDrawable(this,user.getAvatar()));            binding.detailName.setText(user.getName());            binding.detailDesc.setText(String.format("积分:%d 等级:%d",user.getScore(),user.getLevel()));        }        binding.detailAvatar.setVisibility(visibility);        binding.detailName.setVisibility(visibility);        binding.detailDesc.setVisibility(visibility);        binding.detailActionButton.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                if (user == null) login();                else logout();            }        });        binding.detailActionButton.setText(user == null ? "登录":"退出登录";    }}

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
296#
 楼主| 发表于 2016-9-14 10:38 | 只看该作者

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
297#
 楼主| 发表于 2016-9-15 23:09 | 只看该作者
是的,所有View的定义、find、判空都不见了,所有的这些操作都在编译器为我们生成的ActivityDetail2Binding.java中完成,只需要在onCreate时调用如下代码进行setContentView即可实现,

binding = DataBindingUtil.setContentView(this,R.layout.activity_detail2);
1
我的天哪

2.2.4 ActivityDetail2Binding中注入View相关的代码分析
可以在as中方便的查看编译器自动生成的类,这个类位于/app/build/intermediates/classes/debug/com/asha/demo/databinding/ActivityDetail2Binding.class中,缩减掉Binding逻辑后的代码为:

public class ActivityDetail2Binding extends ViewDataBinding {
    private static final IncludedLayouts sIncludes = null;
    private static final SparseIntArray sViewsWithIds = new SparseIntArray();
    public final Button detailActionButton;
    public final ImageView detailAvatar;
    public final TextView detailDesc;
    public final TextView detailName;
    private final LinearLayout mboundView0;
    private long mDirtyFlags = -1L;

    public ActivityDetail2Binding(DataBindingComponent bindingComponent, View root) {
        super(bindingComponent, root, 0);
        Object[] bindings = mapBindings(bindingComponent, root, 5, sIncludes, sViewsWithIds);
        this.detailActionButton = (Button)bindings[4];
        this.detailAvatar = (ImageView)bindings[1];
        this.detailDesc = (TextView)bindings[3];
        this.detailName = (TextView)bindings[2];
        this.mboundView0 = (LinearLayout)bindings[0];
        this.mboundView0.setTag((Object)null);
        this.setRootTag(root);
        this.invalidateAll();
    }
    ...
    static {
        sViewsWithIds.put(2131492948, 1);
        sViewsWithIds.put(2131492949, 2);
        sViewsWithIds.put(2131492950, 3);
        sViewsWithIds.put(2131492951, 4);
    }
}

其中全局静态SparseIntArray数组中存放了4个数字,这个四个数字为R.java中生成的对应View的id,

public final class R {
    ...
    public static final class id {
        ...
        public static final int detail_action_button = 2131492951;
        public static final int detail_avatar = 2131492948;
        public static final int detail_desc = 2131492950;
        public static final int detail_name = 2131492949;
        ...
    }
    ...
}

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
298#
 楼主| 发表于 2016-9-15 23:10 | 只看该作者
在ActvityDetail2Binding实例构造的时候调用了mapBindings,一次解决了所有View的查找,mapBindings函数在ActvityDetail2Binding父类ViewDataBinding中实现。
3 使用表达式在layout.xml中填充model数据
在ActivityDetail2.java中还存在大量的View控制、数据填充代码,如何把这些代码在交给layout.xml完成呢?
3.1 ModelAdapter类
第2节中已经定义了User.java类作为Model类,但是我们经常会遇到Model类和真正View展示不一致的情况,本例子中定义一个来ModelAdapter类来完整Model数据到展示数据的适配。示例代码为ActivityDetail3.java的内部类,可以调用ActivityDetail3.java中的函数,代码定义如下:
public class DetailActivity3 extends AppCompatActivity {    public class ModelAdapter {        private User user;        public ModelAdapter(User user) { this.user = user;}        public String getName(){ return user != null ? user.getName() : null;}        public Drawable getAvatar(){            return user != null ? ContextCompat.getDrawable(DetailActivity3.this,user.getAvatar()) : null;        }        public String getDesc(){            return user != null ? String.format("积分:%d 等级:%d",user.getScore(),user.getLevel()) : null;        }        public String actionText(){ return user != null ? "退出登录" : "登陆"; }        public void clickHandler(View view){            if (user != null) logout();            else login();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
3.2 activity_detail3.xml中使用model
同样复制一份activity_detail2.xml为activity_detail3.xml,在<layout>节点加入<data>节点,并且在里面定义需要用的model类(比如ModelAdapter adapter),当然也可以是基础类型变量(比如int visibility);
随后,就可以在下面的view中使用表达式了,全部布局文件如下:
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable name="adapter" type="com.asha.demo.DetailActivity3.ModelAdapter"/>        <variable name="visibility" type="int"/>    </data>    <LinearLayout        androidrientation="vertical" android:layout_width="match_parent"        android:layout_height="match_parent">        <View            android:background="@color/detail_background"            android:layout_width="match_parent"            android:layout_height="66dp">        </View>        <ImageView            android:src="@{adapter.avatar}"            android:visibility="@{visibility}"            android:id="@+id/detail_avatar"            android:layout_gravity="center"            android:layout_marginTop="-33dp"            android:layout_width="66dp"            android:layout_height="66dp" />        <TextView            android:visibility="@{visibility}"            android:text="@{adapter.name}"            android:id="@+id/detail_name"            android:textSize="17sp"            android:textColor="@color/textColorPrimary"            android:layout_marginTop="15dp"            android:layout_gravity="center"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <TextView            android:visibility="@{visibility}"            android:text="@{adapter.desc}"            android:id="@+id/detail_desc"            android:layout_marginTop="15dp"            android:textSize="13sp"            android:layout_gravity="center"            android:layout_width="wrap_content"            android:layout_height="wrap_content" />        <Button            android:text="@{adapter.actionText}"            androidnClick="@{adapter.clickHandler}"            android:id="@+id/detail_action_button"            android:layout_marginTop="15dp"            android:layout_gravity="center"            android:textColor="@color/white"            android:background="@drawable/selector_g_button"            android:layout_width="220dp"            android:layout_height="wrap_content" />    </LinearLayout></layout>

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
299#
 楼主| 发表于 2016-9-15 23:11 | 只看该作者
3.3 DetailActivity3.java中调用填充
如下代码所示,只需要在登录状态改变的时候,给viewDataBinding设置所需要的adatper、visibility值,即可完成数据的填充
public class DetailActivity3 extends AppCompatActivity {    ActivityDetail3Binding binding;    public class ModelAdapter {        ...    }    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        binding = DataBindingUtil.setContentView(this,R.layout.activity_detail3);        login();    }    private void login(){        fill(User.newInstance());    }    private void logout(){        fill(null);    }    private void fill(final User user){        binding.setAdapter(new ModelAdapter(user));        binding.setVisibility( user != null ? View.VISIBLE : View.GONE);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
3.4 ActivityDetail3Binding中填充相关的代码分析
同样,ActivityDetail3Binding中,编译器根据activity_detail3.xml中的<data>标签,自动生成了诸如setAdapter、setVisibility的代码,setAdapter相关代码如下:
public class ActivityDetail3Binding extends ViewDataBinding{    private ModelAdapter mAdapter;    ...    public void setAdapter(ModelAdapter adapter) {        this.mAdapter = adapter;        synchronized(this) {            this.mDirtyFlags |= 1L;        }        this.notifyPropertyChanged(1);        super.requestRebind();    }    public ModelAdapter getAdapter() {        return this.mAdapter;    }    ...}

使用道具 举报

回复
论坛徽章:
1682
九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-27 15:37:10九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55九尾狐狸
日期:2012-09-19 11:12:55玉石琵琶
日期:2014-06-26 16:52:29玉石琵琶
日期:2014-06-26 16:52:29
300#
 楼主| 发表于 2016-9-15 23:12 | 只看该作者
非常简单,自动生成了getter和setter,在完成set操作后,调用执行notifyPropertyChanged和super.requestRebind()
* notifyPropertyChanged
ViewDataBinding本身就是一个BaseObservable, 在往ViewDataBinding注册观察某个属性的变化,如果注册了mAdapter的变化,对应的观察器就会接收到回调。相关逻辑与反向Binding相关,谷歌官方还没给出相关使用文档,不再深入分析;
  • super.requestRebind()
    1.此函数为ViewDataBinding中的函数,具体实现为判断现在是否有Rebind请求,如果有则return;如果没有则根据运行时sdk版本交给handler或者choreographer插入到下一帧中执行mRebindRunnable。
    2.在mRebindRunnable中会根据当前sdk版本,如果大于等于KITKAT,则需要在onAttachToWindow后执行executePendingBindings;否则直接执行executePendingBindings。
public abstract class ViewDataBinding extends BaseObservable {    protected void requestRebind() {        synchronized (this) {            if (mPendingRebind) {                return;            }            mPendingRebind = true;        }        if (USE_CHOREOGRAPHER) {            mChoreographer.postFrameCallback(mFrameCallback);        } else {            mUIThreadHandler.post(mRebindRunnable);        }    }    /**     * Runnable executed on animation heartbeat to rebind the dirty Views.     */    private final Runnable mRebindRunnable = new Runnable() {        @Override        public void run() {            synchronized (this) {                mPendingRebind = false;            }            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {                // Nested so that we don't get a lint warning in IntelliJ                if (!mRoot.isAttachedToWindow()) {                    // Don't execute the pending bindings until the View                    // is attached again.                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);                    return;                }            }            executePendingBindings();        }    };}

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表