こんにちは!
ポケモン新作の情報解禁でアドレナリンがっぽがぽなちょこひです。
御三家は最初は炎のニャビーかな〜!
どうせサンもムーンも買うからコンプリートするけども!(・∀・) 

 
今日のテーマ!

開設以来Swiftの記事しか書いていなくて、
iOS苦手なのがバレAndroidを放置していたので
今回はAndroidJavaをやります! 

テーマは、ズバリ「Adapter」。

以前Swiftの記事でTableViewControllerについてやりましたが、
AndroidでもListViewを使ってなんやかんやするアレをアレします。

個人的に初見の時はかなり手こずったので、覚え書きとして。 


 
テストアプリ詳細


とある大学のとあるクラスの名簿データjsonファイル(ローカル)から
Adapterを使いListViewに表示。
画面上部のスピナーでクラスを変更する。

以下ss。
ssA


スピナーの値を変えると、


ssB


となります。



以下、用意したjsonファイル。

{
"students": {
"classA": [
{
"no": "IT0101",
"name": "愛 飢え夫",
"type": "いつも悲しい顔をしている。"
},
{
"no": "IT0102",
"name": "華菊 ヶ子",
"type": "クラスのマドンナ。"
},
{
"no": "IT0103",
"name": "差氏 酢背素",
"type": "酢酸を愛している。"
},
{
"no": "IT0104",
"name": "館津 手斗",
"type": "テトリスは世界レベル。"
},
{
"no": "IT0105",
"name": "何抜 寝乃",
"type": "睡眠学習で右に出るものはいない。"
}
],
"classB": [
{
"no": "IT0201",
"name": "ハヒ フヘホ",
"type": "アンパンのような何かに毎日殴られている。"
},
{
"no": "IT0202",
"name": "舞美夢 芽萌",
"type": "クラスのアイドル。かわいい。"
},
{
"no": "IT0203",
"name": "焼ゐ 湯ゑ世",
"type": "焼きおにぎりが好き。"
},
{
"no": "IT0204",
"name": "乱離 流 レロ",
"type": "日本とドバイのハーフ?"
},
{
"no": "IT0205",
"name": "和衣 上ヲ",
"type": "トップスは常に和装。"
}
]
}
}
 
no:学籍番号
name:生徒氏名
type:特徴

とし、すべての情報をListViewの各行に表示させます。


 
XML編


①新規プロジェクトを作成

今回は「AdapterTest」とします。

appName

②activity_main.xmlにパーツを配置

allLayout


layoutTree



③各行に反映させるレイアウトxmlファイルを用意

xml

「New」→「XML」→「Layout XML File」を選び、新規作成します。
タイトルは、「row_item」としました。

rowItem


rowLayout

各パーツにidも振っておきます。


④Spinnerの設定をする

Spinnerに表示する選択肢を設定します。

「res」→「values」→「strings.xml」を開き、
以下を追加します。
<string-array name="spiArray">
<item>A組</item>
<item>B組</item>
</string-array>
配列を作り、各要素をSpinnerに読み込ませる、という流れです。

この配列を、activity_main.xmlにてSpinnerに紐づけます。


<Spinner
android:layout_width="match_parent"
android:entries="@array/spiArray"
android:layout_height="wrap_content"
android:id="@+id/spi"/>
「entries」というステータスがそれに当たります。



Jsonファイルをローカルに保存する

今回はjsonファイルをローカルに使用するので、
プロジェクト自体にぶっこみます。

assets

「New」→「Folder」→「Assets Folder」を選択。
何も変更せず、Finishします。

すると、コンポーネントツリーに「assets」というフォルダができるので、
そこに先ほどのstudents.jsonをペースト。

これでOKです。



 
ソースコード編


・・・の前に、今の私のAdapterの概念を説明します。
信憑性には欠けます。間違えていたらご指摘いただけると幸いです。

blog0511

これの赤文字がAdapterの動き(イメージ)。

表示するデータはjsonファイルに格納されています。
そこからデータを抜き出して、各行のパーツにセットし、
セットしたものをListViewに渡してくれるのがAdapter。たぶん。

つまり超有能。あだぷたーちゃんいい子。


では、ソースコードに行きましょう。


①生徒用のクラスファイルを作成する

makeClass

「New」→「Java Class」より、新規クラスファイルを作成します。
名称は「Student」としました。

public class Student {
private String no;
private String name;
private String type;

public Student() {
}

public String getNo() {
return no;
}

public String getName() {
return name;
}

public String getType() {
return type;
}

public void setNo(String id) {
this.no = id;
}

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

public void setType(String type) {
this.type = type;
}
}
private変数を3つ用意し、
それぞれno(学籍番号)、name(氏名)、type(特徴)とし、
それぞれのconstractor、getter、setterを用意。

※フィールドを用意して「command + N」で
getter、setterのショートカット入力ができるよ!!!


②JSONとソースコードの窓口役のクラスを作る
JSONとの連携をひとつのクラスに任せます。
今回はStudentクラスと同様、「JsonHelper」というクラスを作りました。

public class JsonHelper {
public static ArrayList<Student> parseJson(String strJson, int code) {
ArrayList<Student> ary = new ArrayList<>();
try {
String className = "classA";
if(code == 1) className = "classB";
JSONObject json = new JSONObject(strJson);
JSONObject students = json.getJSONObject("students");
JSONArray list = students.getJSONArray(className);
for(int i = 0; i < list.length(); i++) {
JSONObject entry = list.getJSONObject(i);
ary.add(parseToStudent(entry));
}
}catch (Exception e) {
Log.e("JsonHelper", Log.getStackTraceString(e));
}
return ary;
}

public static Student parseToStudent(JSONObject json) throws JSONException {
Student tmp = new Student();
tmp.setNo(json.getString("no"));
tmp.setName(json.getString("name"));
tmp.setType(json.getString("type"));
return tmp;
}

JsonはDictionaryの様な構造をしています。
キーを指定し、その中身を引っ張ってくる、という流れでデータを受け取ります。

まずは、ArrayList<String> aryにjsonの中身をぶっこみます。

try内、「className」及びその下のif分は、
Spinnerにより表示するクラスを変えるためのものです。
デフォルトではclassAを指定し、初期化する際の引数codeにより判別します。
つまり、MainActivity.javaにてcode変数を変更します(後述)。

jsonを受け取る→その中のstudentsを受け取る→その中からA組かB組どちらかの名簿をlistに格納。
for文内でStudentを初期化し、データとして受け取っていく、という流れ。


③jsonファイルを高速で読み込むgetData()メソッドを実装する
ここで愉快な仲間たちが増えます!!!!!
ちなみにまだまだどういう人なのか全然理解出来ていないので、ふわっと説明します。

・InputStream → ファイルを読み込むクラス(今回はjsonファイル)
・BufferedReader → InputStreamをラップして実際にStreamに入れるクラス
・StringBuilder → 可変長のString。本来Stringは非可変長なため、追加回数が多い場合はStringBuilderで処理をして、最後にStringにキャストしたほうが処理が高速になる。

private String getData() {
String json = "";
BufferedReader br = null;
try {
InputStream in = getAssets().open("students.json");
br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line);
}
json = sb.toString();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}finally {
try {
if(br != null) br.close();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}
}
return json;
}

JsonHelperに引数として渡すメソッドです。
JsonhelperはString型にしたJsonデータと、今回独自に追加したSpinner判定のint型を引数に取ります。
assetsからMainActivity.javaを経由してJsonHelperにアクセスし、Studentにパースして、それらをAdapterによりListViewにセットする、という流れ。

④Adapterを用意する
class RowModelAdapter extends ArrayAdapter<Student> {
RowModelAdapter(Context con) {
super(con, R.layout.row_item);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Student item = getItem(position);
if(convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(R.layout.row_item, null);
}
if(item != null) {
TextView txtNo = (TextView)convertView.findViewById(R.id.txtNo);
if(txtNo != null) {
txtNo.setText(item.getNo() + ":");
}
TextView txtName = (TextView)convertView.findViewById(R.id.txtName);
if(txtName != null) {
txtName.setText(item.getName());
}
TextView txtType = (TextView)convertView.findViewById(R.id.txtType);
if(txtType != null) {
txtType.setText("\t" + item.getType());
}
}
return convertView;
}
}
 Adapterに各行のレイアウトを読み込ませます。
convertViewとは、その読み込まれたViewを指します。
convertViewの中の、それぞれのパーツに、Studentクラスのgetterを使って値をセットしていきます。

⑤ListViewにAdapterをセットする 

private int code = 0;
public void setList() {
RowModelAdapter adapter = new RowModelAdapter(this);
ArrayList<Student> students = JsonHelper.parseJson(getData(), code);
for(Student tmp: students) {
adapter.add(tmp);
}
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(adapter);
}
今回はSpinnerの値が選ばれるごとにListViewを再読み込みさせるため、メソッド化しています。
読み込みが一度でよければ、onCreate()内に記述します。

adapterに各データをaddし、listがそれを参照する様にします。



⑥Spinnerの値が選ばれた時のメソッドを用意する

 
class spinnerValueChanged implements AdapterView.OnItemSelectedListener {
    @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
code = position;
setList();
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
}

OnItemSelectedListenerは、Spinnerの値が変更された時に呼ばれます。
onItemSelected、onNothingSelectedは必ず実装しなければなりませんが、
今回は必要無いので空っぽのメソッドのままにしています。

これをonCreate()でSpinnerにセットしてあげれば、OK。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setList();

Spinner spi = (Spinner)findViewById(R.id.spi);
spi.setOnItemSelectedListener(new spinnerValueChanged());
}





ソースコードまとめ


・MainActivity.java
public class MainActivity extends AppCompatActivity {

private int code = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setList();

Spinner spi = (Spinner)findViewById(R.id.spi);
spi.setOnItemSelectedListener(new spinnerValueChanged());
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

private String getData() {
String json = "";
BufferedReader br = null;
try {
InputStream in = getAssets().open("students.json");
br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line);
}
json = sb.toString();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}finally {
try {
if(br != null) br.close();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}
}
return json;
}

class RowModelAdapter extends ArrayAdapter<Student> {
RowModelAdapter(Context con) {
super(con, R.layout.row_item);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Student item = getItem(position);
if(convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(R.layout.row_item, null);
}
if(item != null) {
TextView txtNo = (TextView)convertView.findViewById(R.id.txtNo);
if(txtNo != null) {
txtNo.setText(item.getNo() + ":");
}
TextView txtName = (TextView)convertView.findViewById(R.id.txtName);
if(txtName != null) {
txtName.setText(item.getName());
}
TextView txtType = (TextView)convertView.findViewById(R.id.txtType);
if(txtType != null) {
txtType.setText("\t" + item.getType());
}
}
return convertView;
}
}

public void setList() {
RowModelAdapter adapter = new RowModelAdapter(this);
ArrayList<Student> students = JsonHelper.parseJson(getData(), code);
for(Student tmp: students) {
adapter.add(tmp);
}
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(adapter);
}

class spinnerValueChanged implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
code = position;
setList();
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
}

・Jsonhelper.java

public class JsonHelper {
public static ArrayList<Student> parseJson(String strJson, int code) {
ArrayList<Student> ary = new ArrayList<>();
try {
String className = "classA";
if(code == 1) className = "classB";
JSONObject json = new JSONObject(strJson);
JSONObject students = json.getJSONObject("students");
JSONArray list = students.getJSONArray(className);
for(int i = 0; i < list.length(); i++) {
JSONObject entry = list.getJSONObject(i);
ary.add(parseToStudent(entry));
}
}catch (Exception e) {
Log.e("JsonHelper", Log.getStackTraceString(e));
}
return ary;
}

public static Student parseToStudent(JSONObject json) throws JSONException {
Student tmp = new Student();
tmp.setNo(json.getString("no"));
tmp.setName(json.getString("name"));
tmp.setType(json.getString("type"));
return tmp;
}
}


・Student.java

public class Student {
private String no;
private String name;
private String type;

public Student() {
}

public String getNo() {
return no;
}

public String getName() {
return name;
}

public String getType() {
return type;
}

public void setNo(String id) {
this.no = id;
}

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

public void setType(String type) {
this.type = type;
}
}



まとめ


長くなりました!
こうしてまとめてみると、まだあやふやなところが目立ちますね。
理解し次第、差し替えていこうと思います。

p.s.最近腰痛が本当に辛いです。なんとかして。




ちょこひ