Slide 1

Slide 1 text

ビンゴアプリハンズオン あんざいゆき(@yanzm) ABC 2019S

Slide 2

Slide 2 text

Yuki Anzai • Android App Developer (2009~) • CEO of uPhyca Inc. (2011~) • Google Developer Expert for Android • Organizer of GTUG Girls and droid girls • Twitter : @yanzm

Slide 3

Slide 3 text

クリック

Slide 4

Slide 4 text

1.クリック 2. Next を クリック

Slide 5

Slide 5 text

My Bingo Finish をクリック com.sample.mybingo デフォルトのままでOK API 21: Android 5.0 Java Use androidx.* artifact にチェック

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

1.ダブルクリック 2. Design をクリック

Slide 8

Slide 8 text

Blueprint を選択

Slide 9

Slide 9 text

クリック

Slide 10

Slide 10 text

0dp (match_constraint) に変更 bottom のハンドルをクリック

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

左右と上の margin を 16 に変更

Slide 13

Slide 13 text

text の Hello World! を削除 プレビュー⽤の text に 1, 2, 3, 4, 5 と⼊⼒

Slide 14

Slide 14 text

id を historyView に変更

Slide 15

Slide 15 text

Palette から Button を drag して下部に drop

Slide 16

Slide 16 text

Infer Constrains をクリック

Slide 17

Slide 17 text

layout_width を 0dp (match_contstrains) にし、margin を変更

Slide 18

Slide 18 text

1. text の右端の⼩さい ボタンをクリック 2. Add new resources, New string Value... をクリック

Slide 19

Slide 19 text

next_number 次の数字を出す OK をクリック

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

Palette から TextView を drag して真ん中あたりに drop

Slide 22

Slide 22 text

上のハンドルを drag して 上部の TextView の ハンドルにつなげる 下のハンドルを drag して 下部の Button の ハンドルにつなげる

Slide 23

Slide 23 text

Infer Constrains をクリック

Slide 24

Slide 24 text

layout_width と layout_height を 0dp (match_contstrains) にし、margin を変更

Slide 25

Slide 25 text

クリックして展開

Slide 26

Slide 26 text

gravity を展開して center を true に変更

Slide 27

Slide 27 text

textSize を 100sp に変更 text を削除してプレビュー の text を 75 に変更

Slide 28

Slide 28 text

id を numberView に変更

Slide 29

Slide 29 text

design にして⾒た⽬を 確認

Slide 30

Slide 30 text

Slide 31

Slide 31 text

ダブルクリック

Slide 32

Slide 32 text

public class MainActivity extends AppCompatActivity { private TextView historyView; private TextView numberView; private View button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); historyView = findViewById(R.id.historyView); numberView = findViewById(R.id.numberView); button = findViewById(R.id.button); } }

Slide 33

Slide 33 text

public class MainActivity extends AppCompatActivity { private TextView historyView; private TextView numberView; private View button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); historyView = findViewById(R.id.historyView); numberView = findViewById(R.id.numberView); button = findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { numberView.setText("" + System.currentTimeMillis()); } }); }

Slide 34

Slide 34 text

public class MainActivity extends AppCompatActivity { private TextView historyView; private TextView numberView; private View button; private final Random random = new Random(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int nextNumber = random.nextInt(75) + 1; numberView.setText("" + nextNumber); } });

Slide 35

Slide 35 text

public class MainActivity extends AppCompatActivity { ... private final List histories = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { ... button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int nextNumber = random.nextInt(75); numberView.setText("" + nextNumber); histories.add(nextNumber); historyView.setText(createHistoryText()); } }); }

Slide 36

Slide 36 text

public class MainActivity extends AppCompatActivity { ... private String createHistoryText() { final StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (int history : histories) { if (isFirst) { isFirst = false; } else { sb.append(", "); } sb.append(history); } return sb.toString(); } }

Slide 37

Slide 37 text

public class MainActivity extends AppCompatActivity { ... private final List pickNumbers = new ArrayList<>(); private final List histories = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pickNumbers.clear(); for (int i = 1; i <= 75; i++) { pickNumbers.add(i); } historyView = findViewById(R.id.historyView); numberView = findViewById(R.id.numberView); button = findViewById(R.id.button);

Slide 38

Slide 38 text

public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { ... button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int index = random.nextInt(pickNumbers.size()); final int nextNumber = pickNumbers.remove(index); numberView.setText("" + nextNumber); histories.add(nextNumber); historyView.setText(createHistoryText()); if (pickNumbers.isEmpty()) { button.setEnabled(false); } }

Slide 39

Slide 39 text

チャレンジ課題

Slide 40

Slide 40 text

ViewModel を使う

Slide 41

Slide 41 text

https://developer.android.com/jetpack/androidx/releases/lifecycle dependencies { ... def lifecycle_version = "2.0.0" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" } build.gradle (Module: app) Android developer ページ → Jetpack → AndroidX → Release Notes → androidx.lifecycle

Slide 42

Slide 42 text

public class MainViewModel extends ViewModel { private final Random random = new Random(); private final List pickNumbers = new ArrayList<>(); private final List histories = new ArrayList<>(); public MainViewModel() { for (int i = 1; i <= 75; i++) { pickNumbers.add(i); } } int pickNextNumber() { final int index = random.nextInt(pickNumbers.size()); final int nextNumber = pickNumbers.remove(index); histories.add(nextNumber); return nextNumber; }

Slide 43

Slide 43 text

String createHistoryText() { final StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (int history : histories) { if (isFirst) { isFirst = false; } else { sb.append(", "); } sb.append(history); } return sb.toString(); } boolean isAllPicked() { return pickNumbers.isEmpty(); } }

Slide 44

Slide 44 text

public class MainActivity extends AppCompatActivity { private TextView historyView; private TextView numberView; private View button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); historyView = findViewById(R.id.historyView); numberView = findViewById(R.id.numberView); button = findViewById(R.id.button); final MainViewModel viewModel = ViewModelProviders.of(this) .get(MainViewModel.class);

Slide 45

Slide 45 text

public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { ... button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int nextNumber = viewModel.pickNextNumber(); numberView.setText("" + nextNumber); historyView.setText(viewModel.createHistoryText()); if (viewModel.isAllPicked()) { button.setEnabled(false); } } }); }

Slide 46

Slide 46 text

LiveData を使う

Slide 47

Slide 47 text

public class MainViewModel extends ViewModel { private final Random random = new Random(); private final List pickNumbers = new ArrayList<>(); private final List histories = new ArrayList<>(); private final MutableLiveData state = new MutableLiveData<>(); public MainViewModel() { pickNumbers.clear(); for (int i = 1; i <= 75; i++) { pickNumbers.add(i); } } LiveData getState() { return state; }

Slide 48

Slide 48 text

void pickNextNumber() { final int index = random.nextInt(pickNumbers.size()); final int nextNumber = pickNumbers.remove(index); histories.add(nextNumber); state.setValue(new State(nextNumber, createHistoryText(), isAllPicked())) } private String createHistoryText() { final StringBuilder sb = new StringBuilder(); boolean isFirst = true; for (int history : histories) { if (isFirst) { isFirst = false; } else { sb.append(", "); } sb.append(history); } return sb.toString(); }

Slide 49

Slide 49 text

private boolean isAllPicked() { return pickNumbers.isEmpty(); } static class State { final int nextNumber; final String historyText; final boolean isAllPicked; State(int nextNumber, String historyText, boolean isAllPicked) { this.nextNumber = nextNumber; this.historyText = historyText; this.isAllPicked = isAllPicked; } } }

Slide 50

Slide 50 text

public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { ... viewModel.getState().observe(this, new Observer() { @Override public void onChanged(MainViewModel.State state) { numberView.setText("" + state.nextNumber); historyView.setText(state.historyText); if (state.isAllPicked) { button.setEnabled(false); } } });

Slide 51

Slide 51 text

public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { ... button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { viewModel.pickNextNumber(); } }); } }