くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Activity破棄問題をサポートしてくれるIcepickとparceler

破棄されやすいAndroidのActivity/Fragmentたち。ライブラリを使うと簡単に状態を保存/復元できるので、その備忘録。

dependency

repositories {
  maven {url "https://clojars.org/repo/"} // Icepick
}
dependencies {
  // Icepick
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
  
  // parceler
  compile 'org.parceler:parceler-api:1.1.9'
  annotationProcessor 'org.parceler:parceler:1.1.9'
}

Icepick

Activity

class ExampleActivity extends Activity {
  @State String username; // 保存するフィールドには、@Stateをつける

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState); // 復元
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState); // 保存
  }
}

Fragment

public class ExampleFragment extends Fragment {
    @State String username;

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
        super.onViewStateRestored(savedInstanceState);
        Icepick.restoreInstanceState(this, savedInstanceState);  // 復元
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Icepick.saveInstanceState(this, outState);    // 保存
    }
}

Custom View

class CustomView extends View {
  @State int selectedPosition;

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state)); // 復元
  }
  
  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());  // 保存
  }
}

parceler

対象のクラスに@Parcelをつける

@Parcel
public class Example {
    public String name;

    public Example(String name) {
        this.name = name;
    }
}

保存/復元はこんな感じ

// 保存
Parcelable wrapped = Parcels.wrap(new Example("Andy")); 

// 復元
Example example = Parcels.unwrap(wrapped);
example.getName(); // Andy

parceler + Icepick

IcepickのBundlerを使ってwrap/unwrapする

@Parcel
public class Example {
    public String name;

    public static class Bundler implements icepick.Bundler<Example> {  // icepickのBundlerを個別に定義
        @Override
        public void put(String key, Example example, Bundle bundle) {
            bundle.putParcelable(key, Parcels.wrap(example));
        }

        @Override
        public Example get(String key, Bundle bundle) {
            return Parcels.unwrap(bundle.getParcelable(key));
        }
    }
}

@State()にBundlerを指定する

public class ExampleFragment extends Fragment {

    @State(Example.Bundler.class) Example example; 
}

参考にしたサイト様

Androidでネットワークの状態を確認/判定する

ネットワーク確認

ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

// 接続状態状況の判定
boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting();
                      
// ネットワークタイプの判定
boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;

ネットワーク確認:Wifiのみ

WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wm.getConnectionInfo();
 
WifiInfo.SupplicantState state = wifiInfo.getSupplicantState();

接続の変化はブロードキャストレシーバーで受け取る

<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>

参考にしたサイト様

Androidでファイル選択してSDカードとかにコピーする

Androidで、選択したファイルをコピーしたいなぁと思ったので、調べたときの備忘録。

大まかな手順は以下な感じ。

  1. ファイルを選択する
  2. コピー先のパスを取得する
  3. UriからFileへコピーする

1. ファイルを選択する

private final static int CHOSE_FILE_CODE = 12345;

// インテントを発行して、ファイル選択Activityを開く
public void onClickButton() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("image/jpeg");
    startActivityForResult(intent, CHOSE_FILE_CODE);
}

// 選択したファイルのUriを受け取る
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == CHOSE_FILE_CODE && resultCode == RESULT_OK && data != null) {
        Uri uri = data.getData();
        // 後は、取得したファイルをごにょごにょする
    }
}

2. コピー先のパスを取得する

File filesDir = activity.getFilesDir(); // '/data/data/${applicationId}/files'のFile
File sdCardDir = Environment.getExternalStorageDirectory(); // SDカードのルートのFile

3. UriからFileへコピーする

// AndoridでUriのファイルをコピーする
// AndroidではUriをFile変換するのは大変なので、InputStreamでコピーする方法にした
public static void copyFile(@NonNull Activity activity,
                            @NonNull Uri src, @NonNull File dest) throws IOException {
    InputStream in = null;
    FileOutputStream out = null;
    try {
        in = activity.getContentResolver().openInputStream(src);
        if (in == null) throw new NullPointerException();

        out = new FileOutputStream(dest);
        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) > 0) out.write(buf, 0, len);
    } finally {
        // compile 'commons-io:commons-io:2.5'をつかってます
        IOUtils.closeQuietly(in);
        IOUtils.closeQuietly(out);
    }
}

以上!!

参考にしたサイト様

JavaでZipする/UnZipするzt-zipがすてき

Javaでzipファイルを扱いたいなとときの備忘録。
zt-zipが簡単そうだったので、その使い方まとめ

dependenciesはこんな感じ

compile 'org.zeroturnaround:zt-zip:1.12'

Zipする

zipする:名前はそのまま。拡張子も変わらない

File targetFile = ....; // zipする対象のFile
ZipUtil.unexplode(targetFile);

zipする:出力先を指定

File targetFile = ....; // zipする対象のFile
File zipFile = ....;     // 作成するzipのFile
ZipUtil.pack(outputDir, zipFile);

zipする:zipする際に、ディレクトリ(dir)配下にファイルを配置

ZipUtil.pack(outputDir, zipFile, new NameMapper() {
    @Override
    public String map(String name) {
        return "dir/" + name;
    }
});

UnZipする

unzipする:名前はそのまま。拡張子も変わらない

File zipFile = ...; // unzipする対象のFile
ZipUtil.explode(zipFile);

unzipする:出力先指定

File zipFile = ....;     // unzipする対象のFile
File outputFile = ....; // 出力先のFile
ZipUtil.unpack(zipFile, outputDir);

使いやすいけど、Orma or lombokと相性がわるい?

Modelでprivateのフィールドに@Setter/@Getterをつけていたが、
dependencyを追加したところで、Ormaが生成したソースで該当のフィールドを参照している部分が、 すべてgetterから直接アクセスに変わっていた。

フィールドの可視性をpublicする形にしているが、微妙。。。時間があればもう少し見たい。

ちなみん、使っていたIDEは、Android Studio

以上!!

参考にしたサイト様

正方形のImageViewをカスタムViewで(Support Library版)

AndroidでRecyclerViewを使って、Gallery的な画面を作りたいなぁと思ったときの備忘録。
ほぼ、うさがにさんの記事の引用。Support LibraryのAppCompat版。

こんな感じ

public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
    public SquareImageView(Context context) {
        super(context);
    }

    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // 縦横サイズ一緒に
        int width = getMeasuredWidth();
        setMeasuredDimension(width, width);
    }
}

参考にしたサイト様

Androidでカレンダーを表示するライブラリ(CompactCalendarView)

Androidでカレンダーアプリを作る際に利用したCompactCalendarViewの備忘録。
いろいろ調べたけど、シンプルで使いやすいライブラリ。

使い方

色やサイズなどは、XMLで設定する感じ

<com.github.sundeepk.compactcalendarview.CompactCalendarView
    android:id="@+id/calendar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    
    <!-- 全体の設定:背景色、文字の色、文字のサイズ -->
    app:compactCalendarBackgroundColor="@android:color/white" 
    app:compactCalendarTextColor="@android:color/brown"
    app:compactCalendarTextSize="16sp"
    
    <!-- 当日の設定:当日の背景色、Indicatorのスタイル、文字の色 -->
    app:compactCalendarCurrentDayBackgroundColor="@android:color/red"
    app:compactCalendarCurrentDayIndicatorStyle="fill_large_indicator"
    app:compactCalendarCurrentDayTextColor="@android:color/white"
    
    <!-- 選択日の設定:選択した日の背景色、Indicatorのスタイル、文字の色 -->
    app:compactCalendarCurrentSelectedDayBackgroundColor="@android:color/white"
    app:compactCalendarCurrentSelectedDayIndicatorStyle="fill_large_indicator"
    app:compactCalendarCurrentSelectedDayTextColor="@android:color/red"
    
    <!-- イベントの設定:イベントのIndicatorのスタイル、3つ以上のイベントがあるときに表示される文字の色 -->
    app:compactCalendarEventIndicatorStyle="small_indicator"
    app:compactCalendarMultiEventIndicatorColor="@android:color/orange"
/>

表示する月やロケールなどは、Java側で設定する感じ

Calendar curDate = Calendar.getInstance().getTime();

CompactCalendarView calendarView.setCurrentDate(curDate); // 現在の日時をセット
calendarView.setFirstDayOfWeek(Calendar.SUNDAY); // 日曜日始まりに設定
calendarView.setLocale(TimeZone.getDefault(), Locale.ENGLISH); // ロケールの設定
calendarView.shouldDrawIndicatorsBelowSelectedDays(true); // 選択した日にEventがある場合でもEventを表示

calendarView.setListener(new CompactCalendarView.CompactCalendarViewListener() {
    @Override
    public void onDayClick(Date dateClicked) {
        // 日付がクリックされたときの処理
    }

    @Override
    public void onMonthScroll(Date firstDayOfNewMonth) {
        // 表示する月が変わったときの処理
    }
});

予定はEventクラスで、CompactCalendarViewに追加する

// 予定(Event)の作成: 
Calendar cal = Calendar.getInstance();
cal.set(2017, 1, 1, 0, 0, 0);
Event event = new Event(
        Color.BLUE,                  // 表示する予定の色
        cal.getTimeInMillis()    // 表示する日時
        );

// カレンダーに予定を追加
calendarView.addEvent(event); 

// カレンダーの予定を全削除
calendarView.removeAllEvents();

他のカレンダーライブラリ

調べた際に見て、他のライブラリ。といっても1つしかない。。

  1. androidcalendarview

いろいろ調べてみたが、自作するのが多い感じ。。

以上!!

参考になるサイト様

Androidで画面サイズを取得する方法

画面サイズを取得する方法を調べたので、その備忘録。
というか、a_nishimuraさんの記事の引用

Display display = activity.getWindowManager().getDefaultDisplay();
Point point = new Point();
display.getSize(point);

displayWidth = point.x;

以上!!

参考にしたサイト様