日々、徒然プログラミング。

都内のIT系専門学校に通う、一人暮らし女子学生chocoffeeの、 日々の気づきと学び、たまにほっこりを綴るブログ。

エンジニアのみなさま、エンジニアの卵のみなさま、IT業界のみなさまが
わたしの「チームってなんだ?」というぼやきに反応してくださることを祈って。

覚え書き

こんにちは!
近所のいつものカフェでご満悦してるちょこひです。

今日突然暑くなった!
朝からルンルンで洗濯回せるのはいいけども
暑いのだいっきらいなんでやめてほしい!


アカン

今日はPHPのsort関数について書きます。
夏休み明けたらすぐ定期テストがあるため最近重い腰を上げて
ようやっと勉強し始めたんですが

昨日見事にソート関連でハマりました。

ググりにググりまくり
友達に確認してなんとかなったので
書き留めておきます。


今日使うもの


さて、sort関数の話をする前にソートされるものを用意しましょう。
今回は2つ用意します。
 
$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$dict = array('chocolate' => 'チョコレート', 'apple' => 'リンゴ', 'banana' => 'バナナ');

$arrayはよくみる配列です。
Javaと同じように、0から順にインデックスが振られます。
要素数3つの配列です。

$dictは「連想配列」です。要素とキーがセットになったもの。
Swiftでは辞書って呼ばれるものです。 


では、ソート関数のお話。
PHPには、いろんなソート関数が用意されています。

・sort
・rsort
・ksort
・krsort
・asort
・arsort

今回はこれらの整理です。 


普通の配列の場合

 まずは普通の配列をソートする場合についてまとめます。

 
$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$result = sort($array);
print_r($array);


実行結果が以下です。

Array
(
    [0] => あいうえお
    [1] => かきくけこ
    [2] => さしすせそ
)

sort関数は、要素を昇順でソートし、インデックスを振りなおします
戻り値はboolean。成功時にtrue、失敗時にfalseが入ります。
ここでは$resultにはtrueが入っています。


では、rsort関数は
$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$result = rsort($array);
print_r($array);
 
実行結果が以下です。

Array
(
    [0] => さしすせそ
    [1] => かきくけこ
    [2] => あいうえお
)

rsort関数は、要素を降順でソートし、インデックスを振り直します
sort関数同様、戻り値はbooleanです。


続いてksort関数。
$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$result = ksort($array);
print_r($array);
 
 
実行結果が以下です。

Array
(
    [0] => かきくけこ
    [1] => さしすせそ
    [2] => あいうえお
)

ksortのkは、「key」の略です。キーを昇順に並べ替えて配列を作成します
ここではkeyは自動で振られたインデックス順なので、実行前と結果が変わりません。

ここまでくればわかるかと思いますが、
krsortは
$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$result = krsort($array);
print_r($array);

実行結果は

Array
(
    [2] => あいうえお
    [1] => さしすせそ
    [0] => かきくけこ
)
こうなります。

では、asort。

$array = array('かきくけこ', 'さしすせそ', 'あいうえお');
$result = asort($array);
print_r($array);
 

実行結果は

Array
(
    [2] => あいうえお
    [0] => かきくけこ
    [1] => さしすせそ
)

asortはsortと違い、
キーと値のセットを維持したまま要素の昇順で並べ替えます
asortは連想配列でやると違いがよくわかるので、連想配列で同じことをしてみます。


連想配列の場合

では、連想配列で同じことをしてみます。
まずはsortから。
$dict = array('chocolate' => 'チョコレート', 'apple' => 'リンゴ', 'banana' => 'バナナ');
$result = sort($dict);
print_r($dict);

実行結果
Array
(
    [0] => チョコレート
    [1] => バナナ
    [2] => リンゴ
)

キーがどっかいきました。

同じように、ksortでも試してみましょう。

$dict = array('chocolate' => 'チョコレート', 'apple' => 'リンゴ', 'banana' => 'バナナ');
$result = ksort($dict);
print_r($dict);

実行結果は

Array
(
    [apple] => リンゴ
    [banana] => バナナ
    [chocolate] => チョコレート
)
キーと値の関係を維持しながらキーの昇順に並べ替えられ、再度インデックスが追加されています。

では、asortは
 
$dict = array('chocolate' => 'チョコレート', 'apple' => 'リンゴ', 'banana' => 'バナナ');
$result = asort($dict);
print_r($dict);

実行結果は

Array
(
    [chocolate] => チョコレート
    [banana] => バナナ
    [apple] => リンゴ
)

キーと値を維持したまま、要素の昇順で再度インデックスが振られています。

連想配列を要素の昇順/降順でソートしたい時、
sort / rsort関数を使ってしまうとキーが失われてしまいます。

困っちゃう。

そんな時に一役買うのがasort / arsort関数ってな感じです。
 
まとめ


エラい硬くなりましたが、こんな感じ。
最初普通にsort関数を使ってキーがどっかいっちゃって、
Σ(゚д゚;) こうなってました。

にしてもややこしいなあ!
PHPはややこしいです。

Javaのありがたみをかみしめながら引き続き勉強します!



ちょこひ 



こんにちは!
週末のお出かけが楽しみなちょこひです。

家族の夏の旅行といえば、ざっくり分けて
山で避暑派と、海で暑さをエンジョイ派とあると思いますが、
我が家はがっつり避暑でした。暑がり家族。

長野県は安曇野にお蕎麦を食べに行きたい!と父に伝えたところ
週末連れて行ってくれるようで。今から楽しみです。
わさび菜の天ぷら本当に美味しいんですよ・・・


このテーマを選んだ理由


と、いうわけで
おいしいお蕎麦によだれを垂らしながら今回も書いていきます。

私のクラスは、今日Swiftの課題提出日でして。
今がっつり授業中ですが(先生ごめんなさい)一通り終わってるので余裕ぶっこいてます。

昨日の残業中、クラスメートに
TableViewの背景をこの画像にしたいんだけど・・・」と 相談されまして。

ちょこひ「それならTableViewのbackgroundに画像設定してrowItemのbackgroundをalphaにFFいれt 」

ちょこひ「・・・・・」

ちょこひ「Androidの話じゃんこれ。Swiftなら〜(storyboardみる)」

ちょこひ「(やべえbackgroundにimage設定できなさそう)」

友達「storyboardで imageView後ろに入れたいんだけど入れられなくてさ〜」

ちょこひ「じゃあソースコードで無理くりいれてみるか」


という背景があり。

無事設定できたので、またまた覚書として。 


やってみる

 
手順としては、
①ソースコードでimageViewを作成
②imageViewにimageを設定
③tableViewのbackgroundViewにimageViewを設定


・まずはシンプルなTableViewControllerをつくる

story

見栄えのためにまったく意味をなさないNavigationViewControllerをEmbed inしてます
実機はこんな感じ。


31



ひたすらに眠い文字列たちは
ソースコード内で用意した配列を使用しています。

 import UIKit

class TableViewController: UITableViewController {
    let array = ["眠い", "ほんと眠い", "とてつもなく眠い", "尋常じゃなく眠い", "寝ながら寝るくらい眠い"]

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return array.count } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) cell.textLabel!.text = array[indexPath.row] return cell } }
cellのidentifier設定をお忘れなく(わたしは忘れてました)

cellIdentifier




・ソースコードでimageViewを生成 

今回背景に選んだフリー素材は

black

こちら。黒くてかっこいい!

みなさんプログラミングするときの背景色にこだわりはありますか?
TwitterClientのときも少し話しましたが、私は断然黒背景派です。
白かったり明るかったりすると目が痛くなってしまう。

この画像を「black.png」として
プロジェクトにぶっこんでます。

//	tableViewController.swift
override func viewDidLoad() { super.viewDidLoad() let image = UIImage(named: "black.png") let imageView = UIImageView(frame: CGRectMake(0, 0, self.tableView.frame.width, self.tableView.frame.height)) imageView.image = image self.tableView.backgroundView = imageView }

UIImageと用意した画像を紐付けて
tableViewのサイズのUIImabeViewをつくり
UIImageViewにUIImageをぶっこんで
それをtableViewの背景にぶっこんでいます。 

こうしてみると全然難しくない。
むしろ簡単。


この時点で実機は

00

こうなります。
アレ?


犯人はcell


tableViewの背景は無事に反映されています。
なぜ行があるところが白くなってしまうのか?

理由は簡単で、
cellにも背景があるからです。
そしてdefaultは白
背景画像の上に白いcellが乗っかってる感じです。

・cellのbackgroundを変更

clearColor

storyboardからcellのbackgroundをclearcolorに設定。


よっしゃこれででk・・・

14


なんもみえねえ。


・cellの文字色を変更
//	tableViewController.swift
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) cell.textLabel!.text = array[indexPath.row] cell.textLabel?.textColor = UIColor.whiteColor() return cell }

今回は白に設定。



できたー!.。゚+.(・∀・)゚+.゚

05


かっこよろしい!
スタイリッシュ!


まとめ

普段storyboardを使ってレイアウト等は決めているので、
ソースコードでViewを生成するのに少し手間取りました。

あれ?これでいいよな?あれ?ってなった。

tableViewの背景画像を入れている状態ってなかなかみないけど
これはこれでかっこいいですね!


ねむい。お蕎麦食べたい。




ちょこひ


 


こんにちは!
プログラミング楽しい!!!!と発狂しそうになったちょこひです。

最近頭打ちの感覚があって結構焦っていたんですが
さっきすんなり課題クリアできてしまって。
嬉しさのあまり叫びそうになりました。

先生が近くにいたので小声で自慢しに行きました(ご満悦)


 
画面を閉じる


さて、今回は画面を閉じるメソッド
navigationcontroller?.popViewControllerAnimated(Bool)」と
dismissViewControllerAnimated(Bool, completion)」の違いを
おぼえがきします。

どちらも「自分自身を閉じる(Viewを閉じる)」
という動きに変わりはないのですが、ちょっとだけ用途が違います。

これ忘れてるといつかハマりそうだったのでちゃんと勉強し直しました。 



結論から言うと
・navigationController?.popViewControllerAnimated(Bool)
 →navigationControllerの階層を一つ戻る
・dismissViewControllerAcnimated(Bool, completion)
 → モーダルビューを閉じる
こうなります。


以下でちょっと詳しい話をしていこうと思います。



NavigationContoller


iOSエンジニアならかなりの頻度で目にするであろうコイツ。
便利ですよね、本当。
TableViewControllerとの相性がほんとにいいと思っています。

NavigationControllerはViewとViewの階層構造を管理してくれるので
・画面遷移の管理が比較的楽になる
・Segueで「show」を選ぶと、そのViewはNavigationControllerの支配下に入る
・ツリー構造で奥へ奥へ入っていく時は無敵
こんな特徴があります。


blog0710_1


こんな感じ。
popViewControllerAnimatedは、Viewを閉じるというより
一つ前の階層に戻るといった感じ。


ModalViewController


対して、モーダルビューは
Segueを設定するときに「present Modaly」を選択します。
これを選択すると、NavigationControllerの支配下に入らず、
ツリー構造とは別に独立して存在するイメージ。

ツリー構造は詳細へ、さらに詳細へ・・・といった場面で使うことが多いですが
モーダルはオプションだったり、詳細ではない画面を設定するときに使用します。

blog0710_2


先ほどの図と組み合わせるとこんな感じ。
dismissViewControllerはpopViewControllerよりも
「自分自身を閉じる」という要素が大きいです。

NavigationController内でdismissViewControllerを書いても全く反応しません。
逆に、モーダルビューでpopViewControllerを書いても反応しません。

同じ動きに見えますが、実際は用途が違います。


まとめ


今回は画面を閉じるときのメソッドについて
ちょこっとまとめました。

実はこれ、さっき課題をやっているときに3分くらいハマりました。
画面を閉じるとき→dismiss、とすぐ出てくるタイプだったので
もう一つのpopViewと両者の違いをおさらいできてよかった。

他にも閉じるメソッドってあったかしら・・・?

何か見つけたらまた追記します。



さて!吉祥寺に予約してたゲーム用品を受け取りに行くぞ〜!


ちょこひ

 

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

 
今日のテーマ!

開設以来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.最近腰痛が本当に辛いです。なんとかして。




ちょこひ 



こんばんは!
猫になりたいちょこひです。

今みたいに充実した日々ももちろん楽しいけど、
たまに猫になって日向でごろにゃーしたくなる。

 
今回作成するテストアプリ 

前回記事の通り、今回はDelegateを使ったサンプルアプリを作成します。
全体的な動きは
・Viewは2つのみ
・1つ目のViewにtextFieldとButtonを配置、Buttonから2つ目のViewにModalとして遷移
・2つ目のViewに LabelとButtonを配置、1つ目のTextFieldに入力された文字列をLabelに表示、Buttonで2つ目のモーダルビューを閉じる

という簡単なものです。

使うDelegateは
2つ目のモーダルビューを閉じる」部分と、
textField入力時、キーボードのreturnボタンでキーボード自体を閉じる
の2点です。

キーボード自体を閉じる動きはDelegateの鉄板。

Segueを通して文字列を渡す動きは、前回のTableViewControllerの復習を兼ねています。 

以下ss。

38



43

returnキーでキーボードの終了。

46

sendボタンで画面遷移。


50

backボタンでモーダルビューの終了。


では、行きましょう。

 
StoryBoard


・ViewController
09
サイズは適当です。上部にtexiField、下部にButtonを置いて、sendに変更。



・ModalViewController

38

こちらも適当。
わかりやすくするために背景色を変えています。


・sendボタンからModalViewへSegueをつなぐ

presentModaly


前回は「Show」を選択していましたが、今回は「Present Modally」を選択。
これで、2つ目のViewはモーダルビューになります。


・SegueにIdentifierを設定

24


今回はmodalSegueというIdentifierをつけました。


・新規ファイル「ModalViewController.swift」を作成

16

今回もcocoaTouchで作成しました。


・ModalViewControllerとModalViewController.swiftをつなげる

44

storyBoardにて参照先クラスを設定します。




・各クラスにそれぞれのパーツをつなげる

2画面モード(名称なんでしたっけ)にして、

ViewControllerのtextField(Outlet)
ModalViewControllerのLabel(Outlet)とButton(Action) を
つなげます。
 
textField
32
「textField」という名称にしました。


Label
06
「label」という名称にしました。


Button
16
「backToFirst」という名称にしました。


これで、ひとまずは準備OK!



 
ModalViewController.swift


では、ソースコードをいじっていきます。

2つありDelegateのうち、ModalViewを閉じる方は

処理を依頼する側=ModalViewController.swift
処理を依頼される側=ViewController.swift 

です。 まずはこちらを先に実装します。


・protocolの宣言

前回記事の通り、protocolとは「依頼する文書」のことです。これを仲介して実際に依頼します。

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


今回は、protocolの名称を「dismissModal」とし、
実際に依頼する処理名を「dismiss」としました。

この時、dismiss()メソッドには何も書きません。引数と、その型、あるなら戻り値のみ書きます。
Javaでいうinterfaceと同じです。

最近、「protocol extention」とかいうものが出たらしく
AndroidJavaでいう抽象クラスのようなものがあるらしいです。聞かなかったことにします。

このprotocolを2つのクラスでなんやかんやして行きます。

具体的には

ModalViewController.swift「自分自身の終了はできないから、ViewControllerに終了を依頼します!」
ViewController.swift「その依頼書受けます!いつでも来やがれ!」

ってな状態にして行きます。


・「私ModalViewControllerはdismissModalプロトコルを持っています!」と宣言

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


class ModalViewController: UIViewController {

    var delegate: dismissModal?


以降、変数delegateはプロトコルを参照するようになります。


・ついでにViewControllerから文字列を受け取るための変数を宣言

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


class ModalViewController: UIViewController {

    var delegate: dismissModal?

    

    @IBOutlet weak var label: UILabel!

    var text: String = ""


今回は、String型変数textを用意しました。

・Labelに変数textをviewDidLoad()で代入

//  ModalViewController.swift

    override func viewDidLoad() {

        super.viewDidLoad()

        label.text = text


        // Do any additional setup after loading the view.

    }


これで、Labelへのデータ受け渡し準備は完了です。


 ・実際に処理を依頼する
モーダルビューの終了は、backボタンで行います。
つまり、backボタンが押されたら、依頼をすればいいですね。

//  ModalViewController.swift

    @IBAction func backToFirst(sender: UIButton) {

        delegate?.dismiss()

    }


オプショナル型にしているのは、
protocolの受け取り先(今回でいうViewController.swift)でdismiss()メソッドが実装されていない場合があるためです。


ここまでで、依頼書を投げるところまでは完了しました。


 
ViewController.swift


次は、受け取る側の処理です。

protocolとは、直訳すると「規約」です。
Javaのinterfaceと同じで、protocol内のメソッドは実装しなければなりません。

受け取った依頼書には、
「dismiss()メソッドを実行してください」と書いてある感じです。
なので、このクラス内でdismiss()メソッドを用意する必要が有ります。


・dismissModalプロトコルを継承

//  ViewController.swift

class ViewController: UIViewController, dismissModal {

 

・継承したprotocol内のメソッド、つまりdismiss()メソッドを実装

//  ViewController.swift

    func dismiss() {

        self.dismissViewControllerAnimated(true, completion: nil)

    } 


・ModalViewControllerの処理の依頼先は自分だ!という宣言

//  ViewController.swift

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "modalSegue" {

            let nextViewController = segue.destinationViewController as! ModalViewController

            nextViewController.delegate = self

        }

    } 


前回のUITableViewControllerでも、遷移先の変数への代入の際に、似た書き方をしました。
今回も復習のために同じことをやるので、

・Segueを通してModalViewController.swiftの変数への代入

//  ViewController.swift

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "modalSegue" {

            let nextViewController = segue.destinationViewController as! ModalViewController

            nextViewController.delegate = self

            nextViewController.text = textField.text ?? "nil"

        }

    }


こうしてしまいましょう。
これで、モーダルビューのDelegate処理が完了しました。
 

キーボードを閉じる処理


残るもうひとつの処理では、
処理を依頼する側=textField
処理を依頼される側=ViewController.swift
となります。

先ほどはプロトコルを作成しましたが、今回はその必要はありません。
すでにtextFieldクラスには、プロトコルが存在しています。

//  UITextField.h
public protocol UITextFieldDelegate : NSObjectProtocol {

    

    @available(iOS 2.0, *)

    optional public func textFieldShouldBeginEditing(textField: UITextField) -> Bool // return NO to disallow editing.

    @available(iOS 2.0, *)

    optional public func textFieldDidBeginEditing(textField: UITextField) // became first responder

    @available(iOS 2.0, *)

    optional public func textFieldShouldEndEditing(textField: UITextField) -> Bool // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end

    @available(iOS 2.0, *)

    optional public func textFieldDidEndEditing(textField: UITextField) // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called

    

    @available(iOS 2.0, *)

    optional public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool // return NO to not change text

    

    @available(iOS 2.0, *)

    optional public func textFieldShouldClear(textField: UITextField) -> Bool // called when clear button pressed. return NO to ignore (no notifications)

    @available(iOS 2.0, *)

    optional public func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.

}


「UITextFieldDelegate」というものです。
これをVIewControllerで継承します。

・UITextFieldDelegateを継承し、viewDidLoad()内で引き受け宣言をする

//  ViewController.swift

class ViewController: UIViewController, dismissModal, UITextFieldDelegate {

    

    @IBOutlet weak var textField: UITextField!


    override func viewDidLoad() {

        super.viewDidLoad()

        textField.delegate = self

        // Do any additional setup after loading the view, typically from a nib.

    }

 
あとは、「キーボードを閉じる」処理を実装すればOKです。


・textFieldShouldReturnメソッドを実装する

//  ViewController.swift

    func textFieldShouldReturn(textField: UITextField) -> Bool {

        textField.resignFirstResponder()

        return true

    }

 

以上で、完了となります。


まとめ


前回のTableViewControllerと似た部分もあったので、
今回は前回よりも簡略化してまとめてみました。 

アウトプットすることで、自分の頭の中も整理されますね。
ちょっと残っていたごちゃごちゃが解消されました!


依頼する側・依頼される側で分けて考えると、
比較的理解しやすいと思います。


はー!ひとつずつ着々と疑問点を解消していこうー!がんばるぞおー!




ちょこひ 

↑このページのトップヘ