手把手教!如何通过API获取数据并放入RecyclerView列表中展示

代码不全,但尽可能详细的讲解了主要实现步骤,希望能对各位看官有所帮助!

步骤:

一、准备一个RecyclerView列表用于展示数据

二、找到对应的API接口并根据Json格式构造用于解析的Gson类

三、创建ViewModel类承担数据操作的责任

四、将解析好的数据放入RecyclerView中

一、准备一个RecyclerView列表用于展示数据

  1. 在闭包中加入相应的依赖库
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc03"
  1. 在对应的xml文件中加入RecyclerView控件

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/news_recyc"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
  2. 为列表中的子项创建一个xml布局页面,再建立一个类存放展示所需的字段

  3. 接下来要为RecyclerView准备一个适配器,新建NewAdapter类继承自RecyclerView.Adapter,重写和加载参数相关的方法,onCreateViewHolder()、onBindViewHolder ()和getItemCount()这三个方法。NewAdapter的泛型数据类型是自定义的ViewHolder。NewAdapter中有一个构造函数,把要展示的数据源传进来,并赋值给一个全局变量,作为数据源。后续操作都在数据源的基础上进行。


  4. onCreateViewHolder()用于创建ViewHolder实例。加载news_item布局,创建ViewHolder实例,并把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回。

    public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        //加载item布局
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_item, parent, false);
        //创建ViewHolder实例
        final ViewHolder holder = new ViewHolder(view);
        //未来:实现下面两个页面跳转到仓库主页的activity
        holder.newView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                News news= newsList.get(position);
                Toast.makeText(v.getContext(), "you will see repo ", Toast.LENGTH_SHORT).show();
            }
        });
       //未来:实现下面两个点击事件跳转到个人主页的activity
        holder.userImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                News news= newsList.get(position);
                Toast.makeText(v.getContext(), "you will see profile of  " + news.getUserName(), Toast.LENGTH_SHORT).show();
            }
        });
        holder.userName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                News news= newsList.get(position);
                Toast.makeText(v.getContext(), "you will see profile of  " + news.getUserName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }
    
  5. onBindViewHolder ()用于对RecyclerView子项的数据进行赋值。我们通过position参数得到当前项的News实例。

    public void onBindViewHolder(final ViewHolder holder, int position) {
        final News news = newsList.get(position);
        holder.userName.setText(news.getUserName());
        holder.time.setText(news.getTime());
        holder.action.setText(news.getAction());
    }
    
  6. getItemCount()统计子项个数,返回数据源的长度。

    public int getItemCount() {
        return newsList.size();
    }
    

二、找到对应的API接口并根据Json格式构造用于解析的Gson类

  1. 通过GraphQL找到想找的API,复制到postman里生成请求代码。获取到的请求代码我把它放到了一个函数里,以便调用和修改。

    public static String getUserNews() {
        return "{\"query\":\"query MyQuery {\\r\\n  viewer {\\r\\n    following(first: 10) {\\r\\n      edges {\\r\\n        node {\\r\\n          avatarUrl(size: 10)\\r\\n          login\\r\\n          starredRepositories {\\r\\n            edges {\\r\\n              starredAt\\r\\n            }\\r\\n            nodes {\\r\\n              nameWithOwner\\r\\n            }\\r\\n          }\\r\\n        }\\r\\n      }\\r\\n    }\\r\\n  }\\r\\n}\",\"variables\":{}}";
    }
    
  2. 通过OkHttp发起HTTP请求,建立网络连接

    • 发起HTTP请求,需要创建一个Request对象。可以在最终的bulid方法前连缀很多方法来丰富Request对象,比如指定服务器返回数据格式为json,将上一步得到的请求代码放入body中。

    • 调用OkHttpClient的newCall(request)来创建一个call对象,重写onFailure和onResponse方法。

      //构造请求
      Request.Builder builder = BaseRequestBuilder.getBuilder();
      MediaType mediaType = MediaType.parse("application/json");
      //Json代码
      RequestBody body = RequestBody.create(mediaType, RequestBodyHelper.getUserNews());
      //构建请求
      Request request = builder.method("POST", body).build();
      OkHttpClient client = OkhttpUtil.getInstance();
      //请求数据
      client.newCall(request).enqueue(new Callback() {
          @Override
          public void onFailure(@NotNull Call call, @NotNull IOException e) {
              doFailure();
          }
      
          @Override
          public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
              Log.d("tag", "List数组创建成功");
              doSuccess(response);
      
          }
      });
      //服务器返回的数据
      String data = response.body().string();
      
  3. 通过Gson方法解析服务器返回的JSON数据

    • 先在闭包中添加依赖项

      implementation 'com.google.code.gson:gson:2.8.6'
      
    • 再用Gson反序列化服务器返回的数据。我获取的数据是一个List数组。先对返回来的数据进行字符串处理,<u>确保待解析的字符串是完整的[XXXXXX]</u>。这里说明一下,{}代表里面的代码块是一个类,[]代表里面的代码块是一个数组。根据在GraphQL中看到的Json格式,从内往外的去定义相应的类和数组,层层嵌套,确保变量命名和Json数据中名字一致。这里用简单的例子举例说明一下吧。

      //String data = response.body().string();服务器返回的数据
      String userJson = " [{'isDeveloper':false,'name':'xiaoqiang','age':26,'email':'578570174@qq.com'}, {'isDeveloper':true,'name':'xiaoqiang123','age':27,'email':'578570174@gmail.com' }]";
      
      Gson gson = new Gson();
       Type userListType = new TypeToken<ArrayList<User>>(){}.getType();
      
      List<User> userList = gson.fromJson(userJson, userListType);
      //这个List将为后续操作的数据源
      
    • 对于 List ,反序列化时必须提供它的Type,通过 Gson 提供的 TypeToken<T>.getType() 方法可以定 义当前List的 Type 。这个Type应为服务器返回数据的最外层所定义的类。

三、创建ViewModel类承担数据操作的责任

ViewModel的生命周期比Activity和Fragment都要长,用ViewModel存储数据不用担心数据保存和恢复的问题。再者UI controller 比如 Activity 、Fragment 是设计用来渲染展示数据、响应用户行为、处理系统的某些交互。如果再要求他去负责加载网络或数据库数据,会让其显得臃肿和难以管理。所以为了简洁、清爽、 丝滑,我们可以分离出数据操作的职责给 ViewModel。下面来看看怎么用ViewModel管理数据。

  1. 创建一个NewsViewModel继承自ViewModel,并在构造函数中初始化了数据源,虽然Google为防止内存泄漏禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用,但是为了在ViewModel里操作UI线程,我从外部传了个Context进来。

         private MutableLiveData<ArrayList<News>> Listdata;
        List<News> newList = new ArrayList<>();
        Context newsContext;
    
        public NewsViewModel() {
            Listdata = new MutableLiveData<>();
            initNewList();
        }
    
        public LiveData<ArrayList<News>> getList() {
            return Listdata;
        }
        private void initNewList(){//上文通过API获取数据源,在doSuccess中完成了数据解析,并调用了setListValue()将数据装入ViewModel的Listdata中
        }
         public void setContext(Context context) {
            newsContext = context;
        }
         public void setListValue() {
            ((MainActivity) newsContext).runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Listdata.setValue((ArrayList<News>) newList);
                }
            });
        }
    
  2. 在UI控制器里(Fragment或Activity)生成一个ViewModel对象。通过对象调用getList()就可以获取数据源进行操作了。

    newsViewModel= new ViewModelProvider(this).get(NewsViewModel.class);
    

四、将解析好的数据放入RecyclerView中

  1. 先把RecyclerView与布局页绑定(我的RecyclerView是放在fragment里的)

    View root = inflater.inflate(R.layout.fragment_news, container, false);
    final RecyclerView newsRecyclView = root.findViewById(R.id.news_recyc);
    LinearLayoutManager layoutManager = new LinearLayoutManager(this.getActivity());
    newsRecyclView.setLayoutManager(layoutManager);
    
  2. 之前已经加载了item的布局页到适配器中,因此需要将适配器与newsRecyclView进行绑定

    //ViewModel里用于刷新UI的观察者,getList()获取到数据后则在这个方法中显示在UI上
    newsViewModel.getList().observe(getViewLifecycleOwner(), new Observer<ArrayList<News>>() {
                @Override
                public void onChanged(@Nullable ArrayList<News> s) {
                  //将数据传入适配器,将适配器绑定到RecyclerView
                    NewAdapter adapter=new NewAdapter(s);
                    newsRecyclView.setAdapter(adapter);
                  //负责页面刷新效果的代码
                    progressBar.setVisibility(View.GONE);
                    newsRecyclView.setVisibility(View.VISIBLE);
                }
            });
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342