代碼不全,但盡可能詳細(xì)的講解了主要實(shí)現(xiàn)步驟,希望能對(duì)各位看官有所幫助!
步驟:
一、準(zhǔn)備一個(gè)RecyclerView列表用于展示數(shù)據(jù)
二、找到對(duì)應(yīng)的API接口并根據(jù)Json格式構(gòu)造用于解析的Gson類
三、創(chuàng)建ViewModel類承擔(dān)數(shù)據(jù)操作的責(zé)任
四、將解析好的數(shù)據(jù)放入RecyclerView中
一、準(zhǔn)備一個(gè)RecyclerView列表用于展示數(shù)據(jù)
- 在閉包中加入相應(yīng)的依賴庫
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc03"
-
在對(duì)應(yīng)的xml文件中加入RecyclerView控件
<androidx.recyclerview.widget.RecyclerView android:id="@+id/news_recyc" android:layout_width="match_parent" android:layout_height="match_parent"/> -
為列表中的子項(xiàng)創(chuàng)建一個(gè)xml布局頁面,再建立一個(gè)類存放展示所需的字段
-
接下來要為RecyclerView準(zhǔn)備一個(gè)適配器,新建NewAdapter類繼承自RecyclerView.Adapter,重寫和加載參數(shù)相關(guān)的方法,onCreateViewHolder()、onBindViewHolder ()和getItemCount()這三個(gè)方法。NewAdapter的泛型數(shù)據(jù)類型是自定義的ViewHolder。NewAdapter中有一個(gè)構(gòu)造函數(shù),把要展示的數(shù)據(jù)源傳進(jìn)來,并賦值給一個(gè)全局變量,作為數(shù)據(jù)源。后續(xù)操作都在數(shù)據(jù)源的基礎(chǔ)上進(jìn)行。
-
onCreateViewHolder()用于創(chuàng)建ViewHolder實(shí)例。加載news_item布局,創(chuàng)建ViewHolder實(shí)例,并把加載出來的布局傳入到構(gòu)造函數(shù)中,最后將ViewHolder的實(shí)例返回。
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { //加載item布局 final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.new_item, parent, false); //創(chuàng)建ViewHolder實(shí)例 final ViewHolder holder = new ViewHolder(view); //未來:實(shí)現(xiàn)下面兩個(gè)頁面跳轉(zhuǎn)到倉庫主頁的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(); } }); //未來:實(shí)現(xiàn)下面兩個(gè)點(diǎn)擊事件跳轉(zhuǎn)到個(gè)人主頁的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; } -
onBindViewHolder ()用于對(duì)RecyclerView子項(xiàng)的數(shù)據(jù)進(jìn)行賦值。我們通過position參數(shù)得到當(dāng)前項(xiàng)的News實(shí)例。
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()); } -
getItemCount()統(tǒng)計(jì)子項(xiàng)個(gè)數(shù),返回?cái)?shù)據(jù)源的長(zhǎng)度。
public int getItemCount() { return newsList.size(); }
二、找到對(duì)應(yīng)的API接口并根據(jù)Json格式構(gòu)造用于解析的Gson類
-
通過GraphQL找到想找的API,復(fù)制到postman里生成請(qǐng)求代碼。獲取到的請(qǐng)求代碼我把它放到了一個(gè)函數(shù)里,以便調(diào)用和修改。
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\":{}}"; } -
通過OkHttp發(fā)起HTTP請(qǐng)求,建立網(wǎng)絡(luò)連接
發(fā)起HTTP請(qǐng)求,需要?jiǎng)?chuàng)建一個(gè)Request對(duì)象??梢栽谧罱K的bulid方法前連綴很多方法來豐富Request對(duì)象,比如指定服務(wù)器返回?cái)?shù)據(jù)格式為json,將上一步得到的請(qǐng)求代碼放入body中。
-
調(diào)用OkHttpClient的newCall(request)來創(chuàng)建一個(gè)call對(duì)象,重寫onFailure和onResponse方法。
//構(gòu)造請(qǐng)求 Request.Builder builder = BaseRequestBuilder.getBuilder(); MediaType mediaType = MediaType.parse("application/json"); //Json代碼 RequestBody body = RequestBody.create(mediaType, RequestBodyHelper.getUserNews()); //構(gòu)建請(qǐng)求 Request request = builder.method("POST", body).build(); OkHttpClient client = OkhttpUtil.getInstance(); //請(qǐng)求數(shù)據(jù) 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數(shù)組創(chuàng)建成功"); doSuccess(response); } }); //服務(wù)器返回的數(shù)據(jù) String data = response.body().string();
-
通過Gson方法解析服務(wù)器返回的JSON數(shù)據(jù)
-
先在閉包中添加依賴項(xiàng)
implementation 'com.google.code.gson:gson:2.8.6' -
再用Gson反序列化服務(wù)器返回的數(shù)據(jù)。我獲取的數(shù)據(jù)是一個(gè)List數(shù)組。先對(duì)返回來的數(shù)據(jù)進(jìn)行字符串處理,<u>確保待解析的字符串是完整的[XXXXXX]</u>。這里說明一下,{}代表里面的代碼塊是一個(gè)類,[]代表里面的代碼塊是一個(gè)數(shù)組。根據(jù)在GraphQL中看到的Json格式,從內(nèi)往外的去定義相應(yīng)的類和數(shù)組,層層嵌套,確保變量命名和Json數(shù)據(jù)中名字一致。這里用簡(jiǎn)單的例子舉例說明一下吧。
//String data = response.body().string();服務(wù)器返回的數(shù)據(jù) 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); //這個(gè)List將為后續(xù)操作的數(shù)據(jù)源 對(duì)于 List ,反序列化時(shí)必須提供它的Type,通過 Gson 提供的 TypeToken<T>.getType() 方法可以定 義當(dāng)前List的 Type 。這個(gè)Type應(yīng)為服務(wù)器返回?cái)?shù)據(jù)的最外層所定義的類。
-
三、創(chuàng)建ViewModel類承擔(dān)數(shù)據(jù)操作的責(zé)任
ViewModel的生命周期比Activity和Fragment都要長(zhǎng),用ViewModel存儲(chǔ)數(shù)據(jù)不用擔(dān)心數(shù)據(jù)保存和恢復(fù)的問題。再者UI controller 比如 Activity 、Fragment 是設(shè)計(jì)用來渲染展示數(shù)據(jù)、響應(yīng)用戶行為、處理系統(tǒng)的某些交互。如果再要求他去負(fù)責(zé)加載網(wǎng)絡(luò)或數(shù)據(jù)庫數(shù)據(jù),會(huì)讓其顯得臃腫和難以管理。所以為了簡(jiǎn)潔、清爽、 絲滑,我們可以分離出數(shù)據(jù)操作的職責(zé)給 ViewModel。下面來看看怎么用ViewModel管理數(shù)據(jù)。
-
創(chuàng)建一個(gè)NewsViewModel繼承自ViewModel,并在構(gòu)造函數(shù)中初始化了數(shù)據(jù)源,雖然Google為防止內(nèi)存泄漏禁止在 ViewModel 中持有 Context 或 activity 或 view 的引用,但是為了在ViewModel里操作UI線程,我從外部傳了個(gè)Context進(jìn)來。
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獲取數(shù)據(jù)源,在doSuccess中完成了數(shù)據(jù)解析,并調(diào)用了setListValue()將數(shù)據(jù)裝入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); } }); } -
在UI控制器里(Fragment或Activity)生成一個(gè)ViewModel對(duì)象。通過對(duì)象調(diào)用getList()就可以獲取數(shù)據(jù)源進(jìn)行操作了。
newsViewModel= new ViewModelProvider(this).get(NewsViewModel.class);
四、將解析好的數(shù)據(jù)放入RecyclerView中
-
先把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); -
之前已經(jīng)加載了item的布局頁到適配器中,因此需要將適配器與newsRecyclView進(jìn)行綁定
//ViewModel里用于刷新UI的觀察者,getList()獲取到數(shù)據(jù)后則在這個(gè)方法中顯示在UI上 newsViewModel.getList().observe(getViewLifecycleOwner(), new Observer<ArrayList<News>>() { @Override public void onChanged(@Nullable ArrayList<News> s) { //將數(shù)據(jù)傳入適配器,將適配器綁定到RecyclerView NewAdapter adapter=new NewAdapter(s); newsRecyclView.setAdapter(adapter); //負(fù)責(zé)頁面刷新效果的代碼 progressBar.setVisibility(View.GONE); newsRecyclView.setVisibility(View.VISIBLE); } });


