キマグレラジオステーション

IT周りで勉強したことをぼんやり残してみます

Android ViewをアニメーションでVisibillity=GONEしたいけど・・・

先に書きますが、本件まだ模索中です。

ここのところ、ブログが止まってしまったのは、
Fragmentというやつに四苦八苦して、
本質がまとまらないうちになんとなくできてしまい、
載せる前にガンガン先に行ってしまったからなんですよね・・・小声。

作っていたアプリはそれっぽい感じにできました。
今は体裁を整えております。
細かい学習ポイントは追々まとめましょう。そうしましょう。
 

で、今悩んでいるのがアニメーション。

並んだViewを条件によって出したり消したりする場合に、
プロパティのVisibillityをいじるのですが、
これを、View.ENABLEにしてしまうと、見えなくなるだけで隙間が開いてしまう。
なので、View.GONEにすれば、消えたViewの場所を詰めてくれる。
入力画面のボタンを、新規なら「登録」「クリア」変更なら「登録」「削除」にしたかったときに学びました。

で、今回やりたいのは検索条件のフィールドを普段はしまっておいて、
ボタンを押すとニュニュニュっと出てきてほしいのです。
先に書いたVisibillityの変更だと、一瞬ででできてしまうので、アニメーションさせたくて。
ニュニュニュがほしいのです。


LinearLayout ll = findViewById(R.id.llsample); //適当なレイアウト
TranslateAnimation trans = new TranslateAnimation( //縦に移動するアニメーション
Animation.RELATIVE_TO_SELF, 0.0f
, Animation.RELATIVE_TO_SELF, 0.0f
, Animation.RELATIVE_TO_SELF, 0.0f
, Animation.RELATIVE_TO_SELF, 50.0f);
trans.setDuration(500); //アニメーション継続時間
trans.setFillAfter(true); //アニメーション後に状態を維持するか
ll.startAnimation(trans); //Viewにアニメーションを実行させる

こんなんを書いてみたけど、見た目上消えるだけで実物はそこに残っているし、
そのレイアウトは消えても隙間は開いたまま。

GONEの状態にゆっくりアニメーションさせるにはどうしたらいいのさ・・・



ブログランキング・にほんブログ村へ FC2Blog Ranking




テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/11/28(金) 15:50:47|
  2. Android
  3. | トラックバック:0
  4. | コメント:0

iOS 8.1.1をiPad2に入れたら息を吹き返した

先日発表されたiOS8.1.1。
なにやらiPhone 4SとiPad2のパフォーマンス改善がなされているようで。

で、入れてみた。

メイン機のiPhone 6は・・・目に見える変化なし。
フラシーボ効果で早くなった気が・・・する?

で、今回の主役のiPad 2さん。
iOS8を入れてから、もう完全にゴミのような挙動、
ご年配のような反応速度になってしまい、本気で売るかまな板にしてやろうかと思っていました。

が。

iOS8.1.1入れてみたらこれがまたてきめん。
スッスッスッス動いてくれる!やったー新しくAir2買ってしまう出費が抑えられたー
いや、完全に眉唾だったのにこんなに利いてくれると信心も深くなりますね。


なぜ、信者なのにAndroid開発をしているんだろう。



ブログランキング・にほんブログ村へ FC2Blog Ranking




  1. 2014/11/25(火) 17:09:57|
  2. iOS
  3. | トラックバック:0
  4. | コメント:0

Android-イベント記録アプリ制作10:リスト表示機能を作る

登録したデータを一覧表示する機能を追加します。

evereco_ucd_02.jpg

ユースケース的にこんな感じに拡張。

まずはデータを取得して加工するModelクラス

EventSelectModel

package jp.sheepman.evereco.model;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import jp.sheepman.evereco.bean.entity.BaseEntity;
import jp.sheepman.evereco.bean.entity.EventEntity;
import jp.sheepman.evereco.bean.form.EventListForm;
import jp.sheepman.evereco.util.DatabaseUtil;
import android.content.Context;

public class EventSelectModel extends BaseModel {
//検索用のSQL
final String sql = "SELECT * FROM event";

public List<EventListForm> selectAll(Context context){
//日付変換用のフォーマット
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//検索結果のコレクション
List<EventListForm> formlist = new ArrayList<EventListForm>();

DatabaseUtil util = new DatabaseUtil(context);
try {
//DBをオープン
util.open();
//SQLのパラメータ(全件検索するのでnull
String[] params = null;
//DatabaseUtilの検索メソッドでsqlを実行
//第三匹数はModelクラスをセットするので、自分をセット
List<BaseEntity> entitylist = util.select(sql, params, this);

//検索結果を処理
for(BaseEntity e : entitylist){
//日付
Date eventDate = sdf.parse(((EventEntity)e).getEvent_date());
//今日からの経過日数を算出
long dateDiff = ((new Date().getTime()) - eventDate.getTime())
/ (1000 * 60 * 60 * 24);
//表示用のFormオブジェクトに値をセット
EventListForm form = new EventListForm();
//DBのPK
form.setId(((EventEntity)e).getId());
//イベント名をセット
form.setEventName(((EventEntity)e).getEvent_name());
//イベント日付をセット
form.setEventDate(((EventEntity)e).getEvent_date());
//経過日数をセット
form.setDateDiff(String.valueOf(dateDiff));
//コレクションにadd
formlist.add(form);
}
} catch (ParseException e1) {
// TODO 自動生成された catch ブロック
e1.printStackTrace();
} finally {
util.close();
}
return formlist;
}

@Override
public BaseEntity getEntity() {
return new EventEntity();
}

}

やってることは単純に検索結果を受け取って、加工して呼び出し元のActivityに返すだけ。

DatabaseUtilに検索用のメソッドを追加。
ちょっと汎用的にしてみた。

DatabaseUtil#select

public List<BaseEntity> select(String sql, String[] params, BaseModel model){
List<BaseEntity> list = new ArrayList<BaseEntity>();
try{
Map<String, Method> map = new HashMap<String, Method>();
for(Method m : model.getEntity().getClass().getDeclaredMethods()){
if(m.getName().startsWith(KEYWORD_SETTER)){
map.put(m.getName()
.replaceFirst(KEYWORD_SETTER, "")
.toLowerCase(Locale.ENGLISH)
, m);
}
}

Cursor c = db.rawQuery(sql, params);
while(c.moveToNext()){
BaseEntity entity = model.getEntity();
for(int i = 0; i < c.getColumnCount(); i++){
String key = c.getColumnName(i)
.replaceFirst(KEYWORD_SETTER, "")
.toLowerCase(Locale.ENGLISH);
if(map.containsKey(key)){
Object value;
if(c.getType(i) == Cursor.FIELD_TYPE_BLOB){
value = c.getBlob(i);
} else if(c.getType(i) == Cursor.FIELD_TYPE_BLOB){
value = c.getBlob(i);
} else if(c.getType(i) == Cursor.FIELD_TYPE_FLOAT){
value = c.getFloat(i);
} else if(c.getType(i) == Cursor.FIELD_TYPE_INTEGER){
value = c.getInt(i);
} else if(c.getType(i) == Cursor.FIELD_TYPE_STRING){
value = c.getString(i);
} else if(c.getType(i) == Cursor.FIELD_TYPE_NULL){
value = null;
} else {
value = c.getString(i);
}
map.get(key).invoke(entity, value);
}
}
list.add(entity);
}
c.close();
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
return list;
}

Entityクラスは親クラスのBaseEntityをかえすようにして、
セットされたModelクラスのgetEntity()から使用する子クラスのEntityを取得。
そのクラスのプロパティに値を入れてListにして返却します。

プロパティの取得は、Entityクラスのsetterメソッドから自動判別。
これである程度汎用的になったかな。

あとは表示用のActivity。

ListActivity

package jp.sheepman.evereco;

import java.util.List;

import jp.sheepman.evereco.adapter.BaseCustomAdapter;
import jp.sheepman.evereco.bean.form.EventListForm;
import jp.sheepman.evereco.model.EventSelectModel;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class ListActivity extends Activity {

private ListView lvEventList;

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

//ListViewを取得
lvEventList = (ListView)findViewById(R.id.lvEventList);
//ListViewのアイテム操作のためのアダプタ
EventListAdapter adapter = new EventListAdapter(this);
//ListViewにアダプタをセット
lvEventList.setAdapter(adapter);
//ListViewのアイテムをクリックした際のイベントをセット
lvEventList.setOnItemClickListener(lsnrEventListItem);

//イベント一覧となるformのリストを取得するModelクラスから取得する
EventSelectModel model = new EventSelectModel();
List list = model.selectAll(this);
//取得したformクラスをListViewに追加する
for(EventListForm form : list){
adapter.add(form);
}
//ListViewへの変更を反映させる
adapter.notifyDataSetChanged();
}

/**
* ListViewのアイテムをクリックしたときのイベントリスナ
*/
private OnItemClickListener lsnrEventListItem = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View item, int position, long id) {
Toast.makeText(item.getContext() , "click", Toast.LENGTH_LONG).show();
}
};

/**
* ListViewのアイテムを操作するためのアダプタ
* @author sheepman
*/
private class EventListAdapter extends BaseCustomAdapter {
//レイアウトをViewに変換するインフレータ
private LayoutInflater inflater;

/**
* コンストラクタ
* @param context
*/
public EventListAdapter(Context context) {
super(context);
inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
}

@Override
public View getView(int position, View currentView, ViewGroup parent) {
EventListForm data = (EventListForm)list.get(position);

if(currentView == null){
currentView = inflater.inflate(R.layout.layout_list_row, null);
}
((TextView)currentView.findViewById(R.id.tvListEventDate)).setText(data.getEventDate());
((TextView)currentView.findViewById(R.id.tvListEventName)).setText(data.getEventName());
((TextView)currentView.findViewById(R.id.tvListDateDiff)).setText(data.getDateDiff());

currentView.setTag(data.getId());

return currentView;
}
}

}

ListViewに自作のAdapterでデータを流し込んであげます。
AdapterのgetViewメソッドをオーバーライドして、
受け取ったデータをセットした一行分のViewを作成してリターン。

レイアウトのXMLは以下。

activity_list.xml

<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"
tools:context="${relativePackage}.${activityClass}" >

<ListView
android:id="@+id/lvEventList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:dividerHeight="20dp"
/>
</RelativeLayout>


layout_list_row.xml

<?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="wrap_content"
xmlns:tools="http://schemas.android.com/tools">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:id="@+id/llListItemText"
>
<TextView
android:id="@+id/tvListEventDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="5"
android:height="50dp"
android:gravity="center" />

<TextView
android:id="@+id/tvListEventName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:height="50dp"
android:gravity="left|center_vertical"
/>
</LinearLayout>
<TextView
android:id="@+id/tvListDateDiff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="15dp"
android:gravity="center"
android:textSize="25sp"
/>
</RelativeLayout>


実行してこんなもん。
evereco_shot_list01.png




ブログランキング・にほんブログ村へ FC2Blog Ranking




テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/11/01(土) 11:48:00|
  2. Android
  3. | トラックバック:0
  4. | コメント:0

Android-イベント記録アプリ制作⑨:役割ごとにBeanを作る

前回までで、なんとなくデータを保存できるようにしたけど、

insertをするメソッドの引数が非常にイケていない感じになっています。


public void insert(Context context, String eventName, String eventDate, String comment){
//処理
}

こんなかんじね。
機能拡張があるととっても変更が面倒だし、JUnitとか回したいときに嫌がられる。はず。

なので、イベント名や日付などの機能固有の値は全てBeanオブジェクトにまとめて、
呼び出し側はそのクラスのオブジェクトに値を詰めて、引数で渡してやるようにすればシンプルになるはず。

前のエントリで、「DTOやEntityクラスを用いて」と、書いていたけど
下記の記事を読んでいて、自分の認識が曖昧だったことを痛感しました。
【JavaBeans】BeanとDTOとEntityとVOとFormの違いって何?

DTOはWebSeriviceなどのリモートとのやりとりに使えるようにするものだし、
EntityはEJB(JPAか。)で用いられるのがメインなんだな。
なので、”Beanを使う”という表現にまとめさせていただきます。かしこ。

やりたいことは、
・Activityクラスと、ModelクラスとのやりとりはformというBeanを用意する
・DBのアクセスはEntityというBeanを用意する
と、レイヤごとにBeanクラスを分けて扱います。

EventInsertForm.java

package jp.sheepman.evereco.bean.form;

/**
* イベント登録用DTO
* @author sheepman
*/
public class EventInsertForm extends BaseForm {
private String eventName;
private String eventDate;
private String comment;
/**
* @return eventName
*/
public String getEventName() {
return eventName;
}
/**
* @param eventName セットする eventName
*/
public void setEventName(String eventName) {
this.eventName = eventName;
}
/**
* @return eventDate
*/
public String getEventDate() {
return eventDate;
}
/**
* @param eventDate セットする eventDate
*/
public void setEventDate(String eventDate) {
this.eventDate = eventDate;
}
/**
* @return comment
*/
public String getComment() {
return comment;
}
/**
* @param comment セットする comment
*/
public void setComment(String comment) {
this.comment = comment;
}
}

プライベートなプロパティをもたせて、setterとgetterがあるだけ。
基本となるガワの以下Beanを継承します。
BaseForm.java

package jp.sheepman.evereco.bean.form;

public abstract class BaseForm {

}


これらを使うと、ModelクラスとActivityクラスは以下のようになります。
EventInsertModel.java

/**
* イベント情報をテーブルにInsertする
* @param contextContext
* @param formEventInsertForm
*/
public void insert(Context context, EventInsertForm form){
//Entityクラスに値をセットする
EventEntity entity = new EventEntity();
entity.setEvent_name(form.getEventName());
entity.setEvent_date(form.getEventDate());
entity.setComment(form.getComment());

DatabaseUtil util = new DatabaseUtil(context);
util.open();
//Entityの内容でInsertする
util.insert("event", entity);
util.close();
}


InputActivity.java#OnClickListener#onClick

@Override
public void onClick(View v) {
//画面の入力項目を取得
String eventName = txtEventName.getText().toString();
String eventDate = txtEventDate.getText().toString();
String comment = txtComment.getText().toString();

//formに値を詰める
EventInsertForm form = new EventInsertForm();
form.setEventName(eventName);
form.setEventDate(eventDate);
form.setComment(comment);

//ビジネスロジッククラスのメソッドを実行
EventInsertModel model = new EventInsertModel();
model.insert(v.getContext(), form);

//入力後は入力欄をクリアする
txtEventName.setText("");
txtEventDate.setText("");
txtComment.setText("");
}


これでそれぞれのクラス間での値の受け渡しはBeanオブジェクトに詰めて渡すようになりました。
Insertの処理では恩恵がわかりづらいけれど、今後のSelectをしてくときにみえてくるはず。たぶん。

ModelのInsertではEntityクラスに値を詰め替えています。
一見煩わしいけど、EntityはDBへのアクセス専用、Formは画面へ表示する用と分けることで、
値を加工したくなった場合に無駄なプロパティを増やしていくことがなくなるのです。

EntityクラスはDBのカラムと名前を一致させる仕様にしました。
EventEntity.java

package jp.sheepman.evereco.bean.entity;

public class EventEntity extends BaseEntity {
private int id;
private String event_name;
private String event_date;
private String comment;

/**
* コンストラクタ
*/
public EventEntity() {
}

/**
* @return id
*/
public int getId() {
return id;
}

/**
* @param id セットする id
*/
public void setId(int id) {
this.id = id;
}

/**
* @return event_name
*/
public String getEvent_name() {
return event_name;
}

/**
* @param event_name セットする event_name
*/
public void setEvent_name(String event_name) {
this.event_name = event_name;
}

/**
* @return event_date
*/
public String getEvent_date() {
return event_date;
}

/**
* @param event_date セットする event_date
*/
public void setEvent_date(String event_date) {
this.event_date = event_date;
}

/**
* @return comment
*/
public String getComment() {
return comment;
}

/**
* @param comment セットする comment
*/
public void setComment(String comment) {
this.comment = comment;
}
}


最後に、ちょっぴりORMっぽくEntityからInsertを自動でセットするメソッドを用意しました。
DatabaseUtil.java#insert

/**
* Insert処理
* @param entity
*/
public void insert(String tablename, BaseEntity entity){
ContentValues values = new ContentValues();
try {
//Entityクラスのメソッド一覧を取得
Method[] methods = entity.getClass().getDeclaredMethods();
for(Method m : methods){
//getterメソッドのみ扱う
if(m.getName().startsWith(KEYWORD_GETTER)){
//カラム名であるkey項目はgetterメソッドのgetを外して小文字にしたもの
String key = m.getName().replaceFirst(KEYWORD_GETTER, "").toLowerCase(Locale.ENGLISH);
//getterメソッドを実行して値を取得する
Object value = m.invoke(entity, null);
//値がNULLでない場合、型別にキャストしてセットする
if(value != null){
if(value instanceof byte[]){
values.put(key, (byte[])value);
} else if(value instanceof Byte){
values.put(key, (Byte)value);
} else if(value instanceof Boolean){
values.put(key, (Boolean)value);
} else if(value instanceof Double){
values.put(key, (Double)value);
} else if(value instanceof Float){
values.put(key, (Float)value);
} else if(value instanceof Integer){
values.put(key, (Integer)value);
} else if(value instanceof Long){
values.put(key, (Long)value);
} else if(value instanceof Short){
values.put(key, (Short)value);
} else {
//上記以外は文字列としてセット
values.put(key, value.toString());
}
} else {
//NULLの場合はNULLをセットする
values.putNull(key);
}
}
}
db.insert(tablename, "-", values);
} catch (IllegalAccessException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}


Entityクラスの親クラスを受け取って、getterメソッドのgetを外したものをカラム名として、
中の値の型別にセットして、SQLiteDatabaseのinsertを呼んであげる。
便利・・・のような、二度手間なような・・・?
まぁこんな感じで大枠は出来てきたのでどんどん拡張していきましょ。





ブログランキング・にほんブログ村へ FC2Blog Ranking




テーマ:プログラミング - ジャンル:コンピュータ

  1. 2014/10/21(火) 23:46:36|
  2. Android
  3. | トラックバック:0
  4. | コメント:0

iOS8.1をアップデートしてみた(10/21 10:00 不具合なし)

今日朝起きたらiOS8.1が来ていたので、即アップグレード。

人柱上等!データ失ったってバックアップあるし。そんなに友達もいないから困らないし。。。

【所要時間】
20分くらい。朝起きてDLしてご飯食べて、インストール押してコーヒー飲んで戻ったら終わってた。

【不具合】
今のところなし。

【良かった点】
・念願のカメラロール復活。これにつきる。
スマホから写真をアップロードするときに、ビミョーに不便だったんだ。

【変わらなかった点】
・Wi-Fiが遅いのが相変わらずの御様子
知人も遅いって言ってたからウチの環境が悪くなったってわけでもないと思うけど・・・
mobilepointがiPhone6に変えてから急に遅くなってしまいマックでの朝コーヒー時にイライラしたり・・・
iOSのせいじゃなきゃごめんなさい。

・使用感
まぁ、大幅に変わったわけでもないので。

【困った/戸惑った点】
・写真の「自分のフォトストリーム」が勝手にONにされてたこと
なぜだかデフォルトでONになっていた。
些細なことだけど・・・
猫の写真で気づいたらiCloudがいっぱいになっちゃったり、
一度フォトストリームに上がったのが消せなかったり、
気づきにくい弊害が多いから個人的にはiCloudへのフォトはOFFにしてたんだけど。

・キーボードに音声入力のボタンが
どうやら音声入力ができるようになったらしく、キーボードの左下の切り替えボタン(地球マーク)のスペースに
音声入力ボタンが割り込んでおる。。。
一般→キーボード→音声入力OFF
で消えてくれた。恥ずかしくて使わないし。。。

後はかえってMacとの連携で遊んでみようかな、という感じ。



ブログランキング・にほんブログ村へ FC2Blog Ranking




  1. 2014/10/21(火) 10:46:19|
  2. iOS
  3. | トラックバック:0
  4. | コメント:0
次のページ