利用PlaceHolderVidew库编写无限列表

利用PlaceHolderVidew库编写无限列表


首先来看一下效果图,以下就是我设想要做的事。

效果图


本次实验目标概述:

1、编写一个json文件里面存放学生的信息,该源将模拟从Internet加载json数据,然后填充到列表项中。

2、当应用程序模拟获取网络数据的时候,列表才会在底部显示加载视图。

3、我们将从Internet中用URL加载图形并将其设置到视图显示中,这里使用了Glide库。

4、json数据将会在应用程序中不断给予学生信息,形成无限列表。


本实验基于安卓PlaceHolderView库,该库具有InfinitePlaceHolderView类能够帮助实现本次设想。

PlaceHolderView库提供5种不同类型的视图

  1. PlaceHolderView
    它建立在RecyclerView之上,并抽象了大多数样板。它通过注释提供API。
  2. InfinitePlaceHolderView
    它基于PlaceHolderView构建,并添加了在用户滚动到列表底部时处理加载更多视图的功能。
  3. ExpandablePlaceHolderView
    它基于PlaceHolderView构建,并创建具有父类与子类关系的ExpandableListView。
  4. SwipePlaceHolderView
    它不是基于RecyclerView构建的。这是一个全新的实现。我们可以使用此类创建各种卡堆叠视图。它提供了可以轻松构建Tinder类的API。
  5. SwipeDirectionalView
    它建立在SwipePlaceHolderView之上。它提供滑动方向和触摸事件的回调。
本次实验用到其中的InfinitePlaceHolderView库,此类基于PlaceHolderView。它定义了更多加载回调和加载视图的方法。




让我们开始构建


步骤1:数据准备及获取联网权限

在android studio中设立一个新项目。

在这里插入图片描述

*build.gradle中*添加依赖项。

android {
    ...
    sourceSets {
        main {
            assets.srcDirs = ['src/main/assets', 'src/main/assets/']
            res.srcDirs = ['src/main/res', 'src/main/res/drawable']
        }
    }
}

dependencies {
    ...
    compile 'com.mindorks:placeholderview:0.7.1'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.github.bumptech.glide:glide:3.7.0'
    compile 'com.google.code.gson:gson:2.7'
}

在src/main目录中创建一个文件夹assets,用来存放json文件(内有学生信息),

在这里插入图片描述

在 mainfests中的AndroidManifest.xml中添加Internet权限,没有该权限不能访问因特网。

< users-permission  android :name = “ android.permission.INTERNET ” />

加在<application .... </application>的后面即可。

在这里插入图片描述

笔记:

什么是JSON:JSON即JavaScript Object Natation, 它是一种轻量级的数据交换格式, 与XML一样, 是广泛被采用的客户端和服务端交互的解决方案.




步骤2:创建src/layout/activity_main.xml

该xml定义了列表的布局,设置列表的布局的整体方向为垂直方向。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <com.mindorks.placeholderview.InfinitePlaceHolderView
        android:id="@+id/loadMoreView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>




步骤3:创建 src/layout/load_more_item_view.xml

该xml定义了列表的每一项的布局。

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="4dp"
    app:cardElevation="1dp"
    android:layout_margin="5dp"
    app:cardBackgroundColor="@android:color/holo_blue_dark">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="20dp"
        android:paddingTop="10dp"
        android:paddingBottom="20dp"
        android:gravity="left">
        <android.support.v7.widget.CardView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            app:cardCornerRadius="4dp"
            app:cardElevation="1dp">
        <ImageView
            android:id="@+id/imageView"
            android:layout_width="60dp"
            android:layout_height="80dp"
            android:layout_gravity="top"
            android:scaleType="centerCrop"/>
        </android.support.v7.widget.CardView>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical">
            <TextView
                android:id="@+id/nameTxt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/white"
                android:textSize="16sp"
                android:typeface="sans"
                android:textStyle="normal"/>
            <TextView
                android:id="@+id/captionTxt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:textColor="@android:color/white"
                android:textSize="12sp"
                android:typeface="sans"
                android:textStyle="normal"/>
            <TextView
                android:id="@+id/gradeTxt"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:layout_gravity="right"
                android:textColor="@android:color/white"
                android:textSize="12sp"
                android:typeface="sans"
                android:textStyle="normal"/>
        </LinearLayout>
    </LinearLayout>
</android.support.v7.widget.CardView>

列表项的布局大致长这样:最上面的是学生名字,中间的是图片,正下方是学生信息,右下角是年级。

列表项布局




步骤4:创建 src/layout/load_more_view.xml

该xml是加载界面布,当从json中获取数据时,显示加载图标。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:padding="5dp">
    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

布局大致长这样:

加载布局




步骤5:将infinite_list.json文件放入步骤1中创建的assets文件夹中并写入学生信息。

name是学生姓名,image_url是图片地址,caption是学生的信息,grade是年级信息。

json形式放置种子文件非常容易解析为模型。

[
	{
		"name" : "吴家鹏",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学数学与信息学院学生,学号116072017032",
		"grade" : "2017级"
	},
	{
		"name" : "陈彬",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学法学院学生,学号116072017031",
		"grade" : "2017级"
	},
	{
		"name" : "赵文鑫",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学化学院学生,学号116072017001",
		"grade" : "2018级"
	},
	{
		"name" : "王文忠",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"name" : "福建师范大学生命科学学院学生,学号116072017040",
		"grade" : "2016级"
	},
	{
		"name" : "张瑞建",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学法学院研究生,学号116072017030",
		"grade" : "2014级"
	},
	{
		"name" : "闫少奇",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学法学院研究生,学号116072017022",
		"grade" : "2015级"
	},
	{
		"name" : "张少",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学生命科学学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name" : "何文",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学网络学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name" : "杨子琪",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学文学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name" : "陈晨",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学教育学生,学号116072017040",
		"grade" : "2017级"
	},
	{
		"name" : "杨超",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学音乐学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name" : "林志强",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学生命科学学院学生,学号116072017040",
		"grade": "2018级"
	},
	{
		"name" : "叶存浩",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学物理学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name": "林振铭",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"caption" : "福建师范大学音乐学院学生,学号116072017040",
		"grade" : "2018级"
	},
	{
		"name" : "林腾翔",
		"image_url" : "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=11813822,2468845429&fm=26&gp=0.jpg",
		"name" : "福建师范大学文学院学生,学号116072017040",
		"grade" : "2018级"
	}
]


笔记:

其实JSON数据就是一段字符串,只不过有不同意义的分隔符将其分割开来而已,其中

[] 代表的是一个数组;

{} 代表的是一个对象;
“ ” 表示的是属性值;
:代表的是前后之间的关系,冒号前面是属性的名称,后面是属性的值,这个值可以是基本数据类型,也可以是引用数据类型。




步骤6:创建Utils.java

utils.java包含解析种子json文件填充模型所需的方法InfiniteFeedInfo.java

public class Utils {

    private static final String TAG = "Utils";

    public static List<InfiniteFeedInfo> loadInfiniteFeeds(Context context){
        try{
            GsonBuilder builder = new GsonBuilder();
            Gson gson = builder.create();
            JSONArray array = new JSONArray(loadJSONFromAsset(context, "infinite_news.json"));
            List<InfiniteFeedInfo> feedList = new ArrayList<>();
            for(int i=0;i<array.length();i++){
                InfiniteFeedInfo feed = gson.fromJson(array.getString(i), InfiniteFeedInfo.class);
                feedList.add(feed);
            }
            return feedList;
        }catch (Exception e){
            Log.d(TAG,"seedGames parseException " + e);
            e.printStackTrace();
            return null;
        }
    }

    private static String loadJSONFromAsset(Context context, String jsonFileName) {
        String json = null;
        InputStream is=null;
        try {
            AssetManager manager = context.getAssets();
            Log.d(TAG,"path "+jsonFileName);
            is = manager.open(jsonFileName);
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            json = new String(buffer, "UTF-8");
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
        return json;
    }
}

笔记:

如果要将一个json数据转换为对象的话需要使用fromJson(String json, Class classOfT) /fromJson(String json, Type typeOfT),当我们将对象转换为String的时候根据String toJson(Object src)方法。这里需要注意一点,如果response.body().string()调用大于一次的话,就会报错java.lang.IllegalStateException: closed,因为response.body().string()调用一次之后流就断掉了,需要重新构建一个response。




步骤7:建立模型InfiniteFeedInfo.java

public class InfiniteFeedInfo {

    @SerializedName("name")
    @Expose
    private String name;

    @SerializedName("image_url")
    @Expose
    private String imageUrl;

    @SerializedName("caption")
    @Expose
    private String caption;

    @SerializedName("grade")
    @Expose
    private String grade;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public String getCaption() {
        return caption;
    }

    public void setCaption(String caption) {
        this.caption = caption;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

笔记:

1、首先说一下SerializedName的主要作用:属性重命名,可以将json中的属性名转为我们自己自定义的属性名其次@SerializedName注解提供了两个属性,上面用到了其中一个’value’,别外还有一个属性’alternate’:接收一个String数组,alternate数组中出现任意一个属性名都可以转换为自定义的属性,如果出现多个则以最后一个为准。@SerializedName 注解属于gson类,读取json文件变量并将其绑定到模型变量。

2、@Expose用于使变量对于gson的解析变为可读。




步骤8:现在,我们将创建类以绑定列表及其操作的项目视图。

ItemView.java 自定义生成JavaBean。

@Layout(R.layout.load_more_item_view)
public class ItemView {

    @View(R.id.nameTxt)
    private TextView nameTxt;

    @View(R.id.captionTxt)
    private TextView captionTxt;

    @View(R.id.gradeTxt)
    private TextView gradeTxt;

    @View(R.id.imageView)
    private ImageView imageView;

    private InfiniteFeedInfo mInfo;
    private Context mContext;

    public ItemView(Context context, InfiniteFeedInfo info) {
        mContext = context;
        mInfo = info;
    }

    @Resolve
    private void onResolved() {
        nameTxt.setText(mInfo.getName());
        captionTxt.setText(mInfo.getCaption());
        gradeTxt.setText(mInfo.getGrade());
        Glide.with(mContext).load(mInfo.getImageUrl()).into(imageView);
    }
}

笔记:

  1. @layout 用于将xml布局与此类绑定。

  2. @View 用于将视图绑定到我们要引用的布局中。

  3. @Resolve当视图位于内存中时调用一个方法,以便用数据填充该视图。

  4. Glide 用法传承目前流行的链式调用,他的基础用法如下。

    /**
     * 基础用法.
     */
    private void baseUsed(){
        Glide.with(this)
                .load(URL)
                .into(mImageView);
    }
    




步骤9:现在,我们将创建类以绑定并定义“加载更多”视图。

创建LoadMoreView.java类,为了模拟从Internet延迟加载数据,我定义了ForcedWaitedLoading类。此类创建一个新线程,然后在2秒后,在UI线程中添加更多数据视图列表,该视图应始终从UI线程更新。

@Layout(R.layout.load_more_view)
public class LoadMoreView {

    public static final int LOAD_VIEW_SET_COUNT = 6;

    private InfinitePlaceHolderView mLoadMoreView;
    private List<InfiniteFeedInfo> mFeedList;

    public LoadMoreView(InfinitePlaceHolderView loadMoreView, List<InfiniteFeedInfo> feedList) {
        this.mLoadMoreView = loadMoreView;
        this.mFeedList = feedList;
    }

    @LoadMore
    private void onLoadMore(){
        Log.d("DEBUG", "onLoadMore");
        new ForcedWaitedLoading();
    }

    class ForcedWaitedLoading implements Runnable{

        public ForcedWaitedLoading() {
            new Thread(this).start();
        }

        @Override
        public void run() {

            try {
                Thread.currentThread().sleep(2000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    int count = mLoadMoreView.getViewCount();
                    Log.d("DEBUG", "count " + count);
                    for (int i = count - 1;
                         i < (count - 1 + LoadMoreView.LOAD_VIEW_SET_COUNT) && mFeedList.size() > i;
                         i++) {
                        mLoadMoreView.addView(new ItemView(mLoadMoreView.getContext(), mFeedList.get(i)));

                        if(i == mFeedList.size() - 1){
                            mLoadMoreView.noMoreToLoad();
                            break;
                        }
                    }
                    mLoadMoreView.loadingDone();
                }
            });
        }
    }
}

笔记:

  1. @LoadMore当列表滚动到最后一项时,将调用此注释。任何获取更多数据的请求都会调用此带注释的方法。
  2. mLoadMoreView.loadingDone()当所有数据都已获取并且视图已填充新数据时,需要调用此方法。它将删除加载视图。
  3. mLoadMoreView.noMoreToLoad()提取数据后,应调用此方法。它将停用加载功能。




步骤10:创建 MainActivity.java

我们获取的实例使用InfiniteFeedInfo模型列表中的数据添加视图。

使用setLoadMoreResolver方法添加LoadMoreView要用作加载指示符的对象。

public class MainActivity extends AppCompatActivity {

    private InfinitePlaceHolderView mLoadMoreView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLoadMoreView = (InfinitePlaceHolderView)findViewById(R.id.loadMoreView);
        setupView();
    }

    private void setupView(){

        List<InfiniteFeedInfo> feedList = Utils.loadInfiniteFeeds(this.getApplicationContext());
        Log.d("DEBUG", "LoadMoreView.LOAD_VIEW_SET_COUNT " + LoadMoreView.LOAD_VIEW_SET_COUNT);
        for(int i = 0; i < LoadMoreView.LOAD_VIEW_SET_COUNT; i++){
            mLoadMoreView.addView(new ItemView(this.getApplicationContext(), feedList.get(i)));
        }
        mLoadMoreView.setLoadMoreResolver(new LoadMoreView(mLoadMoreView, feedList));
    }
}



然后我们点击运行。

这样我们就完成了!!!

在这里插入图片描述






参考资料:

PlaceHolderView库介绍文献:https://janishar.com/PlaceHolderView/docs/introduction.html

json数据解析:https://www.open-open.com/bbs/view/1319448346593

网络图片获取:https://blog.csdn.net/qq_42777804/article/details/103515640

  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
<p style="color:#333333;"> <strong> </strong> </p> <p style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="color:#337FE5;">[为什么要学习Spring Cloud微服务]</span> </strong> </p> <p style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="color:#4D555D;"></span> </strong> </p> <p class="ql-long-24357476" style="font-family:"color:#222226;font-size:14px;background-color:#FFFFFF;"> <strong><span style="font-family:"background-color:#FFFFFF;">SpringCloud作为主流微服务框架,<span style="color:#4D555D;">已成为各互联网公司的首选框架,国内外企业占有率持续攀升,</span>是Java工程师的必备技能。</span><span style="font-family:"background-color:#FFFFFF;">就连大名鼎鼎的阿里巴巴</span><span style="font-family:"background-color:#FFFFFF;">dubbo</span><span style="font-family:"background-color:#FFFFFF;">也正式更名为</span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud Alibaba</span><span style="font-family:"background-color:#FFFFFF;">,成为了</span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud </span><span style="font-family:"background-color:#FFFFFF;">微服务中的一个子模块。</span><span style="font-family:"background-color:#FFFFFF;"></span><span style="font-family:"background-color:#FFFFFF;">Spring Cloud是企业架构转型、个人能力提升、架构师进阶的不二选择。</span> </strong> </p> <p style="color:#333333;"> <strong><strong><br /> </strong> </strong> </p> <strong><span style="font-family:"color:#337FE5;font-size:14px;background-color:#FFFFFF;">【推荐你学习这门课的理由】</span><br /> </strong> <p> <br /> </p> <p> <span>1、</span><span style="color:#222226;font-family:"font-size:14px;background-color:#FFFFFF;">本课程总计</span><span style="background-color:#FFFFFF;">29</span><span style="color:#222226;font-family:"font-size:14px;background-color:#FFFFFF;">课时,<span style="color:#333333;">从微服务是什么、能够做什么开始讲起,绝对的零基础入门</span></span><span></span> </p> <p> <span style="background-color:#FFFFFF;">2、<span style="color:#333333;">课程附带全部26个项目源码,230页高清PDF正版课件</span><span style="color:#333333;"></span></span> </p> <p> <span style="background-color:#FFFFFF;"><b><br /> </b></span> </p> <p> <span style="background-color:#FFFFFF;"><b><span style="color:#337FE5;">【课程知识梳理】</span></b></span> </p> <p> <span style="background-color:#FFFFFF;"><b>1、</b></span><span style="color:#333333;">先讲解了什么是单体架构、什么是微服务架构、他们之间有什么区别和联系,各自有什么优缺点。</span> </p> <p> <span style="color:#333333;">2、</span><span style="color:#333333;">从本质入手,使用最简单的Spring Boot搭建微服务,让你认清微服务是一种思想和解决问题的手段,而不是新兴技术。</span> </p> <p style="color:#333333;"> 3、讲解Spring Boot 与Spring Cloud 微服务架构之间的联系,原生的RestTemplate工具,以及Actuator监控端点的使用。 </p> <p style="color:#333333;"> 4、带着微服务所带来的各种优缺点,为大家引入服务发现与注册的概念和原理,从而引入我们的第一个注册中心服务Eureka。 </p> <p style="color:#333333;"> 5、引入负载均衡的理念,区分什么是服务端负载均衡,什么是客户端负载均衡,进而引入Ribbon负载均衡组件的详细使用。 </p> <p style="color:#333333;"> 6、为了解决微服务之间复杂的调用,降低代码的复杂度,我们引入了Feign声明式客户端,让你几行代码搞定服务的远程调用。 </p> <p style="color:#333333;"> 7、最后为大家介绍了整个微服务体系应该包含什么,学习路线是什么,应该学习什么。 </p> <p style="color:#333333;"> <strong><br /> </strong> </p> <p style="color:#333333;"> <strong><span style="color:#337FE5;">【</span><strong><span style="color:#337FE5;">学习方法</span></strong><span style="color:#337FE5;"></span><span style="color:#337FE5;">】</span></strong> </p> <p style="color:#333333;"> 每一节课程均有代码,最好的方式是静下心来,用一天的时间,或者两个半天时间来学习。 </p> <p style="color:#333333;"> 一边听我的讲解,一边使用我提供的项目代码进行观察和运行。 </p> <p style="color:#333333;"> 只要你能跟住我的节奏,你就可以搞定微服务。 </p> <br />
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值