看板 Knuckles_note
作者 標題 [AndroidStudio] 自訂Adapter用JSON資料建立ListView
時間 2015-11-30 Mon. 17:36:16
從網路上抓了熱門文章的JSON資料後
接下來要用 ListView 顯示出來
先複製一個預設的圖示放到 /res/drawable
下載圖示 http://i.disp.cc/disp/displogo300.png
複製後在 /res/drawable 按右鍵選貼上
(之前放過的話不用再放一次)
有用到第三方函式庫 Picasso
如果之前沒有加過的話,要在 build.gradle (Module:app)
裡的 dependencies { 裡加上
compile 'com.squareup.picasso:picasso:2.5.2'
修改 activity_main.xml
將 <RelativeLayout> 的android:padding上下左右改為"0dp"
加上 android:background="#000" 將背景改為黑色
刪除 <TextView …/> 改為 <ListView .../>
修改結果像這樣
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns: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:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:background="#000"
tools:context="com.xxx.dispbbs.MainActivity">
<ListView
android:id="@+id/main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#555"
android:dividerHeight="1px" />
</RelativeLayout>
<RelativeLayout xmlns: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:paddingBottom="0dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingTop="0dp"
android:background="#000"
tools:context="com.xxx.dispbbs.MainActivity">
<ListView
android:id="@+id/main_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#555"
android:dividerHeight="1px" />
</RelativeLayout>
因為要自訂 ListView 裡每個 Row 的版位
要再開一個 xml 檔來設定
在 res/layout 點右鍵選 New > Layout resource file
![[圖]](http://i.imgur.com/UO7GURD.png)
名稱輸入「row_main」,root element輸入「RelativeLayout」
![[圖]](http://i.imgur.com/5SG6NbA.png)
將 <RelativeLayout 的屬性 android:layout_height 由 "match_parent" 改為 "75dp"
然後在 <RelativeLayout …> 與 </RelativeLayout> 中間插入
一個<ImageView/>和兩個<TextView/>,像這樣
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="75dp">
<ImageView
android:id="@+id/img_thumb"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="0dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/img_thumb"
android:layout_alignTop="@+id/img_thumb"
android:textColor="#9CF"
android:textSize="14dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/text_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_title"
android:layout_alignLeft="@+id/text_title"
android:textColor="#FFF"
android:textSize="12dp"/>
</RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="75dp">
<ImageView
android:id="@+id/img_thumb"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="0dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/text_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/img_thumb"
android:layout_alignTop="@+id/img_thumb"
android:textColor="#9CF"
android:textSize="14dp"
android:textStyle="bold"/>
<TextView
android:id="@+id/text_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_title"
android:layout_alignLeft="@+id/text_title"
android:textColor="#FFF"
android:textSize="12dp"/>
</RelativeLayout>
顯示的結果會像這樣
![[圖]](http://i.imgur.com/9N5H12S.png)
要將資料加進 ListView,必需先將資料轉為 Adapter
再傳給 ListView,因為 ListView 不接受其他種類的資料
這邊我們要在 ListView 裡用JSON資料產生自訂格式的 Row
所以要自己寫一個繼承 BaseAdapter 的類別
在 java/com.xxx.dispbbs (或是建立project時輸入的package name) 點右鍵
點選 New > Java Class
![[圖]](http://i.imgur.com/22YgFpL.png)
名稱輸入 MainAdapter 代表這是給主頁面的ListView使用的 Adapter
![[圖]](http://i.imgur.com/8mQKeXI.png)
將自動產生的內容
class MainAdapter {
加上繼承 BaseAdapter
class MainAdapter extends BaseAdapter {
依紅線提示按alt+Enter自動加上缺少的成員函式
![[圖]](http://i.imgur.com/aV5S0T2.png)
點OK,自動加上這四個成員函式
![[圖]](http://i.imgur.com/bSVkJX5.png)
然後在 class MainAdapter extends BaseAdapter { 的下一行輸入以下程式
private LayoutInflater mInflater;
private JSONArray mJsonArray;
// 用來儲存row裡每個view的id,以免每次都要取一次
private static class ViewHolder {
ImageView thumbImageView;
TextView titleTextView;
TextView descTextView;
}
// 類別的建構子
MainAdapter(Context context) {
mInflater = LayoutInflater.from(context);
mJsonArray = new JSONArray();
}
// 輸入JSON資料
void updateData(JSONArray jsonArray) {
mJsonArray = jsonArray;
notifyDataSetChanged();
}
private JSONArray mJsonArray;
// 用來儲存row裡每個view的id,以免每次都要取一次
private static class ViewHolder {
ImageView thumbImageView;
TextView titleTextView;
TextView descTextView;
}
// 類別的建構子
MainAdapter(Context context) {
mInflater = LayoutInflater.from(context);
mJsonArray = new JSONArray();
}
// 輸入JSON資料
void updateData(JSONArray jsonArray) {
mJsonArray = jsonArray;
notifyDataSetChanged();
}
前面建立了三個成員變數
Context mContext; 和 LayoutInflater mInflater; 是在物件建立時要初始化的東西
mContext 就是建立這個 Adapter 的 Activity
mInflater 可用 LayoutInflater.from(mContext); 取得,在抓取Layout ID時會用到
第三個 JSONArray mJsonArray; 就是用來存下載的 JSON 資料
接著建立了一個類別 ViewHolder 用來存列表每個 Row 裡的三個View
ViewHolder是用來在避免ListView裡產生太多Row,要回收沒有用到的Row時用的
接著的 public MainAdapter(Context context, LayoutInflater inflater) {
是類別的建構子,在產生Adaptiter物件時要提供這兩個參數
並初始化 mJsonArray
最後的 public void updateData(JSONArray jsonArray){
就是在程式下載了JSON資料後,用這個成員函式將JSON資料傳入Adapter裡
再利用 notifyDataSetChanged(); 通知系統有資料更新了
再來還要修改之前自動產生的四個成員函式
改成這樣
@Override
public int getCount() {
return mJsonArray.length();
}
@Override
public Object getItem(int position) {
return mJsonArray.optJSONObject(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 檢查view是否已存在,如果已存在就不用再取一次id
if (convertView == null) {
// Inflate the custom row layout from your XML.
convertView = mInflater.inflate(R.layout.row_main, parent, false);
// create a new "Holder" with subviews
holder = new ViewHolder();
holder.thumbImageView = (ImageView) convertView.findViewById(R.id.img_thumb);
holder.titleTextView = (TextView) convertView.findViewById(R.id.text_title);
holder.descTextView = (TextView) convertView.findViewById(R.id.text_desc);
// hang onto this holder for future recyclage
convertView.setTag(holder);
} else {
// skip all the expensive inflation/findViewById
// and just get the holder you already made
holder = (ViewHolder) convertView.getTag();
}
// 取得目前這個Row的JSON資料
JSONObject jsonObject = (JSONObject) getItem(position);
String imageUrl = "";
if (jsonObject.has("img_list")) {
JSONArray img_list = jsonObject.optJSONArray("img_list");
if(img_list.length()!=0) {
imageUrl = img_list.optString(0);
}
}
if (!imageUrl.equals("")) {
// 使用 Picasso 來載入網路上的圖片
// 圖片載入前先用placeholder顯示預設圖片
Picasso.with(mContext).load(imageUrl).placeholder(R.drawable.displogo300).into(holder.thumbImageView);
} else { // 沒有縮圖的話放 disp logo
holder.thumbImageView.setImageResource(R.drawable.displogo300);
}
// 從JSON資料取得標題和摘要
String title = "";
String desc = "";
if (jsonObject.has("title")) {
title = jsonObject.optString("title");
}
if (jsonObject.has("desc")) {
desc = jsonObject.optString("desc");
}
// 將標題和摘要顯示在TextView上
holder.titleTextView.setText(title);
holder.descTextView.setText(desc);
return convertView;
}
public int getCount() {
return mJsonArray.length();
}
@Override
public Object getItem(int position) {
return mJsonArray.optJSONObject(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 檢查view是否已存在,如果已存在就不用再取一次id
if (convertView == null) {
// Inflate the custom row layout from your XML.
convertView = mInflater.inflate(R.layout.row_main, parent, false);
// create a new "Holder" with subviews
holder = new ViewHolder();
holder.thumbImageView = (ImageView) convertView.findViewById(R.id.img_thumb);
holder.titleTextView = (TextView) convertView.findViewById(R.id.text_title);
holder.descTextView = (TextView) convertView.findViewById(R.id.text_desc);
// hang onto this holder for future recyclage
convertView.setTag(holder);
} else {
// skip all the expensive inflation/findViewById
// and just get the holder you already made
holder = (ViewHolder) convertView.getTag();
}
// 取得目前這個Row的JSON資料
JSONObject jsonObject = (JSONObject) getItem(position);
String imageUrl = "";
if (jsonObject.has("img_list")) {
JSONArray img_list = jsonObject.optJSONArray("img_list");
if(img_list.length()!=0) {
imageUrl = img_list.optString(0);
}
}
if (!imageUrl.equals("")) {
// 使用 Picasso 來載入網路上的圖片
// 圖片載入前先用placeholder顯示預設圖片
Picasso.with(mContext).load(imageUrl).placeholder(R.drawable.displogo300).into(holder.thumbImageView);
} else { // 沒有縮圖的話放 disp logo
holder.thumbImageView.setImageResource(R.drawable.displogo300);
}
// 從JSON資料取得標題和摘要
String title = "";
String desc = "";
if (jsonObject.has("title")) {
title = jsonObject.optString("title");
}
if (jsonObject.has("desc")) {
desc = jsonObject.optString("desc");
}
// 將標題和摘要顯示在TextView上
holder.titleTextView.setText(title);
holder.descTextView.setText(desc);
return convertView;
}
public int getCount() {
用來取得資料的筆數,回傳 mJsonArray.length(); 即可
public Object getItem(int position) {
用來取得第 position 筆的資料,回傳 mJsonArray.optJSONObject(position);
public long getItemId(int position) {
用來取得第 position 筆資料的 ID,直接用 position 回傳即可
public View getView(int position, View convertView, ViewGroup parent) {
最重要的一個成員函式,用來設定這筆資料要怎麼顯示
輸入的參數 convertView 是用來做Row的回收機制
若是 null 的話,代表沒有可回收的Row,
所以用 mInflater 取得版位的id建立新的 convertView
將Row裡的三個View用 convertView.setTag() 存起來
若 convertView 不是 null 的話,代表有可回收的Row能用
用 convertView.getTag() 取出View即可
接著就是取出JSON資料裡,第position筆資料
將縮圖、標題、摘要,設定在Row裡的三個View來顯示
其中縮圖用到了 Picasso 來將圖片網址顯示出來
要先判定JSON資料裡有沒有縮圖網址
沒有的話就改成用APP的圖示 displogo300 來顯示
整個 MainAdapter.java 最後就是修改成這樣
package com.xxx.dispbbs;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONObject;
class MainAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private JSONArray mJsonArray;
// 用來儲存row裡每個view的id,以免每次都要取一次
private static class ViewHolder {
ImageView thumbImageView;
TextView titleTextView;
TextView descTextView;
}
// 類別的建構子
MainAdapter(Context context) {
mInflater = LayoutInflater.from(context);
mJsonArray = new JSONArray();
}
// 輸入JSON資料
void updateData(JSONArray jsonArray) {
mJsonArray = jsonArray;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mJsonArray.length();
}
@Override
public Object getItem(int position) {
return mJsonArray.optJSONObject(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 檢查view是否已存在,如果已存在就不用再取一次id
if (convertView == null) {
// Inflate the custom row layout from your XML.
convertView = mInflater.inflate(R.layout.row_main, parent, false);
// create a new "Holder" with subviews
holder = new ViewHolder();
holder.thumbImageView = (ImageView) convertView.findViewById(R.id.img_thumb);
holder.titleTextView = (TextView) convertView.findViewById(R.id.text_title);
holder.descTextView = (TextView) convertView.findViewById(R.id.text_desc);
// hang onto this holder for future recyclage
convertView.setTag(holder);
} else {
// skip all the expensive inflation/findViewById
// and just get the holder you already made
holder = (ViewHolder) convertView.getTag();
}
// 取得目前這個Row的JSON資料
JSONObject jsonObject = (JSONObject) getItem(position);
String imageUrl = "";
if (jsonObject.has("img_list")) {
JSONArray img_list = jsonObject.optJSONArray("img_list");
if(img_list.length()!=0) {
imageUrl = img_list.optString(0);
}
}
if (!imageUrl.equals("")) {
// 使用 Picasso 來載入網路上的圖片
// 圖片載入前先用placeholder顯示預設圖片
Picasso.with(mContext).load(imageUrl).placeholder(R.drawable.displogo300).into(holder.thumbImageView);
} else { // 沒有縮圖的話放 disp logo
holder.thumbImageView.setImageResource(R.drawable.displogo300);
}
// 從JSON資料取得標題和摘要
String title = "";
String desc = "";
if (jsonObject.has("title")) {
title = jsonObject.optString("title");
}
if (jsonObject.has("desc")) {
desc = jsonObject.optString("desc");
}
// 將標題和摘要顯示在TextView上
holder.titleTextView.setText(title);
holder.descTextView.setText(desc);
return convertView;
}
}
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import org.json.JSONArray;
import org.json.JSONObject;
class MainAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private JSONArray mJsonArray;
// 用來儲存row裡每個view的id,以免每次都要取一次
private static class ViewHolder {
ImageView thumbImageView;
TextView titleTextView;
TextView descTextView;
}
// 類別的建構子
MainAdapter(Context context) {
mInflater = LayoutInflater.from(context);
mJsonArray = new JSONArray();
}
// 輸入JSON資料
void updateData(JSONArray jsonArray) {
mJsonArray = jsonArray;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mJsonArray.length();
}
@Override
public Object getItem(int position) {
return mJsonArray.optJSONObject(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 檢查view是否已存在,如果已存在就不用再取一次id
if (convertView == null) {
// Inflate the custom row layout from your XML.
convertView = mInflater.inflate(R.layout.row_main, parent, false);
// create a new "Holder" with subviews
holder = new ViewHolder();
holder.thumbImageView = (ImageView) convertView.findViewById(R.id.img_thumb);
holder.titleTextView = (TextView) convertView.findViewById(R.id.text_title);
holder.descTextView = (TextView) convertView.findViewById(R.id.text_desc);
// hang onto this holder for future recyclage
convertView.setTag(holder);
} else {
// skip all the expensive inflation/findViewById
// and just get the holder you already made
holder = (ViewHolder) convertView.getTag();
}
// 取得目前這個Row的JSON資料
JSONObject jsonObject = (JSONObject) getItem(position);
String imageUrl = "";
if (jsonObject.has("img_list")) {
JSONArray img_list = jsonObject.optJSONArray("img_list");
if(img_list.length()!=0) {
imageUrl = img_list.optString(0);
}
}
if (!imageUrl.equals("")) {
// 使用 Picasso 來載入網路上的圖片
// 圖片載入前先用placeholder顯示預設圖片
Picasso.with(mContext).load(imageUrl).placeholder(R.drawable.displogo300).into(holder.thumbImageView);
} else { // 沒有縮圖的話放 disp logo
holder.thumbImageView.setImageResource(R.drawable.displogo300);
}
// 從JSON資料取得標題和摘要
String title = "";
String desc = "";
if (jsonObject.has("title")) {
title = jsonObject.optString("title");
}
if (jsonObject.has("desc")) {
desc = jsonObject.optString("desc");
}
// 將標題和摘要顯示在TextView上
holder.titleTextView.setText(title);
holder.descTextView.setText(desc);
return convertView;
}
}
修改 MainActivity.java
在 public class MainActivity … { 這行下面加上兩個成員變數
ListView mListView;
MainAdapter mAdapter;
MainAdapter mAdapter;
在成員函式 onCreate(){} 裡的 loadData(); 這行前面加上
mListView = (ListView) findViewById(R.id.main_listview);
mAdapter = new MainAdapter(this);
mListView.setAdapter(mAdapter);
mAdapter = new MainAdapter(this);
mListView.setAdapter(mAdapter);
在成員函式 loadData(){} 裡,下載JSON檔後要執行的 onSuccess(){} 裡的最後加上
if( response.has("err") && response.optInt("err")!=0 ){
Toast.makeText(getApplicationContext(),"Data error", Toast.LENGTH_LONG).show();
}
JSONArray list = response.optJSONArray("list");
if(list==null){
Toast.makeText(getApplicationContext(),"Data error", Toast.LENGTH_LONG).show();
}
mAdapter.updateData(list);
Toast.makeText(getApplicationContext(),"Data error", Toast.LENGTH_LONG).show();
}
JSONArray list = response.optJSONArray("list");
if(list==null){
Toast.makeText(getApplicationContext(),"Data error", Toast.LENGTH_LONG).show();
}
mAdapter.updateData(list);
執行結果
![[圖]](http://i.imgur.com/ZJkhmb2.png)
參考:
http://www.raywenderlich.com/78578/android-tutorial-for-beginners-part-3
http://www.cnblogs.com/devinzhang/archive/2012/01/20/2328334.html
http://www.vogella.com/tutorials/AndroidListView/article.html
--
※ 作者: Knuckles 時間: 2015-11-30 17:36:16
※ 編輯: Knuckles 時間: 2017-01-06 12:26:19
※ 看板: KnucklesNote 文章推薦值: 0 目前人氣: 0 累積人氣: 3825
回列表(←)
分享