웹_프론트_백엔드/JAVA프레임윅기반_풀스택

2020.04.20

shine94 2020. 4. 20. 09:13

1. [과제] RecyclerView를 활용하여 정보를 입력한 뒤 '추가' 버튼 누르면 아래의 리스트에 추가되기

** manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.a008_practice">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

** activity_main 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="정보 추가"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TableLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvName"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="이름 : "
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <EditText
                android:id="@+id/etName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:hint="이름을 입력하세요"
                android:inputType="textPersonName" />
        </TableRow>


        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvAge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="나이 : "
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <EditText
                android:id="@+id/etAge"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:hint="나이를 입력하세요"
                android:inputType="number" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tvAddress"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="주소 : "
                android:textAppearance="@style/TextAppearance.AppCompat.Large" />

            <EditText
                android:id="@+id/etAddress"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ems="10"
                android:hint="주소를 입력하세요"
                android:inputType="textPostalAddress" />
        </TableRow>
    </TableLayout>

    <Button
        android:id="@+id/btnInsert"
        style="@style/TextAppearance.AppCompat.Display1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="추가"
        android:background="#FFEB3B"
        android:layout_margin="3dp"
        android:textColor="#000000" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

** student_item 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="183dp"
        app:cardBackgroundColor="#FFFFFFFF"
        app:cardCornerRadius="10dp"
        app:cardElevation="5dp"
        app:cardUseCompatPadding="true">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/nameTitle"
                android:layout_width="95dp"
                android:layout_height="30dp"
                android:layout_marginStart="25dp"
                android:layout_marginTop="25dp"
                android:src="@drawable/name_img"
                app:layout_constraintEnd_toStartOf="@+id/tvName"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tvName"
                android:layout_width="200dp"
                android:layout_height="30dp"
                android:layout_marginTop="25dp"
                android:layout_marginEnd="4dp"
                android:text="name"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textColor="#000000"
                app:layout_constraintEnd_toStartOf="@+id/btnDelItem"
                app:layout_constraintTop_toTopOf="parent" />

            <ImageView
                android:id="@+id/ageTitle"
                android:layout_width="95dp"
                android:layout_height="30dp"
                android:layout_marginStart="25dp"
                android:layout_marginTop="15dp"
                android:src="@drawable/age_img"
                app:layout_constraintEnd_toStartOf="@+id/tvAge"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/nameTitle" />

            <TextView
                android:id="@+id/tvAge"
                android:layout_width="210dp"
                android:layout_height="30dp"
                android:layout_marginTop="15dp"
                android:layout_marginEnd="44dp"
                android:text="age"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textColor="#000000"
                android:textSize="18sp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tvName" />

            <ImageView
                android:id="@+id/addressTitle"
                android:layout_width="95dp"
                android:layout_height="32dp"
                android:layout_marginStart="25dp"
                android:layout_marginTop="15dp"
                android:src="@drawable/address_img"
                app:layout_constraintEnd_toStartOf="@+id/tvAddress"
                app:layout_constraintHorizontal_bias="0.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/ageTitle" />

            <TextView
                android:id="@+id/tvAddress"
                android:layout_width="210dp"
                android:layout_height="30dp"
                android:layout_marginTop="25dp"
                android:layout_marginEnd="44dp"
                android:layout_marginBottom="25dp"
                android:text="address"
                android:textAppearance="@style/TextAppearance.AppCompat.Large"
                android:textColor="#000000"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tvAge"
                app:layout_constraintVertical_bias="0.466" />

            <ImageButton
                android:id="@+id/btnDelItem"
                android:layout_width="37dp"
                android:layout_height="39dp"
                android:layout_marginTop="15dp"
                android:layout_marginEnd="12dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@android:drawable/ic_delete" />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>

** MainActivity 클래스

package com.example.a008_practice;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    StudentBookAdapter adapter;
    RecyclerView rv;
    EditText etName, etAge, etAddress;

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

        etName = findViewById(R.id.etName);
        etAge = findViewById(R.id.etAge);
        etAddress = findViewById(R.id.etAddress);


        rv = findViewById(R.id.rv);


        // RecyclerView 를 사용하기 위해서는 LayoutManager 지정해주어야 한다.
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        rv.setLayoutManager(layoutManager);

        // Adapter 객체 생성
        adapter = new StudentBookAdapter();

        // 추가 버튼, 데이터 생성
        Button btnInsert = findViewById(R.id.btnInsert);

        btnInsert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = etName.getText().toString();
                String age = etAge.getText().toString();
                String address = etAddress.getText().toString();
                adapter.addItem(new StudentBook(name, age, address));

            }
        });

        rv.setAdapter(adapter); // RecyclerView 에 Adapter 장착


    } // end onCreate()


} // and Activity

** StudentBook 클래스

package com.example.a008_practice;

import java.io.Serializable;

// 학생들 명단을 담을 클래스
public class StudentBook implements Serializable {
    String name;    // 이름
    String age;    // 나이
    String address; // 주소

    public StudentBook() {
    }

    public StudentBook(String name, String age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }


    // getter, setter
    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

} // end StudentBook

** StudentBoodAdapter 클래스

package com.example.a008_practice;

import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

// Adapter 객체 정의
// 데이터를 받아서 각 item 별로 View 생성
public class StudentBookAdapter extends RecyclerView.Adapter<StudentBookAdapter.ViewHolder> {

    List<StudentBook> items = new ArrayList<StudentBook>();

    static StudentBookAdapter adapter;

    // Adapter 생성자
    public StudentBookAdapter() {this.adapter = this;}



    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inf = LayoutInflater.from(parent.getContext());
        View itemView = inf.inflate(R.layout.studnet_item, parent, false);

        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        StudentBook item = items.get(position); // List<> 의 get()
        holder.setItem(item);

    }

    @Override
    public int getItemCount() {
            return items.size();
        }




    // nested class (static inner) 로 ViewHolder 클래스 정의
        static class ViewHolder extends RecyclerView.ViewHolder {

            // ViewHolder 에 담긴 각각의 View 들을 담을 변수
            TextView tvName, tvAge, tvAddress;
            ImageButton btnDelItem;
            ImageView nameTitle, ageTitle, addressTitle;

        // 생성자 필수
        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            // View 객체 가져오기
            nameTitle = itemView.findViewById(R.id.nameTitle);
            ageTitle = itemView.findViewById(R.id.ageTitle);
            addressTitle = itemView.findViewById(R.id.addressTitle);

            tvName = itemView.findViewById(R.id.tvName);
            tvAge = itemView.findViewById(R.id.tvAge);
            tvAddress = itemView.findViewById(R.id.tvAddress);

            btnDelItem = itemView.findViewById(R.id.btnDelItem);

            // 삭제버튼 누르면 item 삭제되게 하기
            btnDelItem.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    adapter.removeItem(getAdapterPosition());  // 데이터 삭제
                    // 데이터 변경 (수정, 삭제, 추가) 내역이 adapter 에 반영되어야 정상적으로 동작함!!! ★★★
                    adapter.notifyDataSetChanged();
                }
            });

        } // end 생성자

        // StudentBook 데이터 받아서 멤버 변수 세팅
        @SuppressLint("ResourceType")
        public void setItem(StudentBook item) {
            tvName.setText(item.getName());
            tvAge.setText(item.getAge());
            tvAddress.setText(item.getAddress());
        }

    } // end ViewHolder


    // 데이터를 다루기 위한 메소드들
    // ArrayList 의 메소드들 사용
    public void addItem(StudentBook item) {
        items.add(item);
    }

    public void addItem(int position, StudentBook item) {items.add(position, item);}
    public void setItems(ArrayList<StudentBook> item) {this.items = item;}
    public StudentBook getItem(int position) {return items.get(position);}
    public void setItem(int position, StudentBook item) {items.set(position, item);}
    public void removeItem(int position) {items.remove(position);}


} // end StudentBookAdapter

 

** 사진 추가

** 과제하면서 깨달은 점 
1) 안드로이드에서 사진 추가할때 소문자로 구성해야 한다는 점
   (처음에 대문자로 했다가 앱이 뻗어버림) 
2) student_item 레이아웃을 만들면서 Constraint의 layout에서의 Target은 2개 이상 잡아야 한다는 점
   (Target을 하나만 잡아서 앱 실행해 보니깐 내가 설정한 위치가 아닌 엉뚱한 곳에서 보여졌음)

 

 

2. A009_sound 모듈

1) MainActivity 액티비티, activity_main 레이아웃

[추가] 필요한 음악 다운 받기

 : res 선택 후 우클릭 > New > Android Resource Directory 클릭
   > Directory name 작성 후 > OK 클릭하면 폴더 생성
   > 생성된 폴더에 필요한 음악 복사 붙여넣기

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a009_sounds">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SoundPool : 음향효과"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <View
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:background="?android:attr/listDivider" />

    <Button
        android:id="@+id/btnPlay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="14dp"
        android:text="play 1" />

    <Button
        android:id="@+id/btnPlay2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="14dp"
        android:text="play 2" />

    <Button
        android:id="@+id/btnPlay3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="14dp"
        android:text="play 3" />

    <Button
        android:id="@+id/btnStop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="STOP" />

</LinearLayout>
package com.lec.android.a009_sounds;

import androidx.appcompat.app.AppCompatActivity;

import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/** 음향: SoundPool
 *      짧은 음향 리소스(들)을 SoundPool 에 등록(load)하고, 원할때마다 재생(play)
 *
 *  res/raw 폴더 만들고  음향 리소스 추가하기
 */

public class MainActivity extends AppCompatActivity {

    private SoundPool sp;

    // 음향 리소스 id
    private final int [] SOUND_RES = {R.raw.gun, R.raw.gun2, R.raw.gun3};

    // 음향 id 값
    int [] soundId = new int [SOUND_RES.length];

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

        Button btnPlay1 = findViewById(R.id.btnPlay);
        Button btnPlay2 = findViewById(R.id.btnPlay2);
        Button btnPlay3 = findViewById(R.id.btnPlay3);
        Button btnStop = findViewById(R.id.btnStop);

        // SoundPool 객체 생성
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            // API21 이상에서는 아래와 같이 SoundPool 생성
            sp = new SoundPool.Builder().setMaxStreams(10).build();
        } else {
            sp = new SoundPool(1,    // 재생 음향 최대 개수
                    AudioManager.STREAM_MUSIC,    // 재생 미디어 타입
                    0   // 재생 품질.. (안쓰임. 디폴트 0)
            );
        }

        // SoundPool 에 음향 리소스들을 load
        for(int i = 0; i < SOUND_RES.length; i++){

            soundId[i] = sp.load(this,  // 현재 화면의 제어권자
                    SOUND_RES[i],  // 음원 파일 리소스
                    1      // 재생 우선순위
            );

        } // end for


        btnPlay1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 음향 재생
                sp.play(soundId[0],   // 준비한 sound 리소스 id
                        1,  // 왼쪽볼륨 float 0.0 ~ 1.0
                        1,  // 오른쪽 볼륨 float
                        0,     // 우선순위 int
                        0,       // 반복회수 int,   0:반복안함   -1:무한반복
                        1f       // 재생속도 float,   0.5(절반속도) ~ 2.0 (2배속)
                );
            }
        });

        btnPlay2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 음향 재생
                sp.play(soundId[1],   // 준비한 sound 리소스 id
                        1,  // 왼쪽볼륨 float 0.0 ~ 1.0
                        0,  // 오른쪽 볼륨 float
                        0,     // 우선순위 int
                        2,       // 반복회수 int,   0:반복안함   -1:무한반복
                        2f       // 재생속도 float,   0.5(절반속도) ~ 2.0 (2배속)
                );
            }
        });

        btnPlay3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 음향 재생
                sp.play(soundId[2],   // 준비한 sound 리소스 id
                        0,  // 왼쪽볼륨 float 0.0 ~ 1.0
                        1,  // 오른쪽 볼륨 float
                        0,     // 우선순위 int
                        -1,       // 반복회수 int,   0:반복안함   -1:무한반복
                        0.5f       // 재생속도 float,   0.5(절반속도) ~ 2.0 (2배속)
                );
            }
        });


        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                for(int i = 0; i < soundId.length; i++){
                    // 음향 정지
                    sp.stop(soundId[i]);
                }
            }
        });



    } // end onCreate()

} // end Actitity

 

2) Main2Activity 액티비티, activity_main2 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a009_sounds">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <activity android:name=".Main2Activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="MediaPlayer"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/btnPlay"
            android:layout_width="55dp"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_media_play"
            android:text="시작" />

        <ImageButton
            android:id="@+id/btnPause"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_media_pause"
            android:text="일시정지" />

        <ImageButton
            android:id="@+id/btnResume"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_menu_slideshow"
            android:text="재시작" />

        <ImageButton
            android:id="@+id/btnStop"
            android:layout_width="57dp"
            android:layout_height="match_parent"
            android:src="@android:drawable/checkbox_off_background"
            android:text="종료" />

    </LinearLayout>

    <SeekBar
        android:id="@+id/sb"
        style="@style/Widget.AppCompat.SeekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnStop"
        android:layout_alignLeft="@+id/btnStop"
        android:layout_marginTop="18dp" />

</LinearLayout>
package com.lec.android.a009_sounds;

import androidx.appcompat.app.AppCompatActivity;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;

public class Main2Activity extends AppCompatActivity {

    private ImageView btnPlay;
    private ImageView btnPause;
    private ImageView btnResume;
    private ImageView btnStop;
    SeekBar sb; // 음악 재생위치를 나타내는 시크바

    MediaPlayer mp; // 음악 재생을 위한 객체

    int pos;    // 재생 위치
    boolean isTracking = false;

    class MyThread extends Thread {
        @Override
        public void run() {
            // SeekBar 가 음악 재생시, 움직이게 하기
            while(mp.isPlaying()){  // 현재 재생 중이라면
                pos = mp.getCurrentPosition();    // 현재 재생중인 위치 ms (int)
                if(!isTracking) {
                    sb.setProgress(pos);    // SeekBar 이동 --> onProgressChanged() 호출함함
               }
            }

        }
    }

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

        btnPlay = findViewById(R.id.btnPlay);
        btnPause = findViewById(R.id.btnPause);
        btnResume = findViewById(R.id.btnResume);
        btnStop = findViewById(R.id.btnStop);
        sb = findViewById(R.id.sb);

        btnPlay.setVisibility(View.VISIBLE);
        btnPause.setVisibility(View.INVISIBLE);
        btnResume.setVisibility(View.INVISIBLE);
        btnStop.setVisibility(View.INVISIBLE);

        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            // SB 값 변경될 때마다 호출
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                // 음악이 끝까지 연주 완료되었다면
                if(seekBar.getMax() == progress && !fromUser) {
                    btnPlay.setVisibility(View.VISIBLE);
                    btnPause.setVisibility(View.INVISIBLE);
                    btnResume.setVisibility(View.INVISIBLE);
                    btnStop.setVisibility(View.INVISIBLE);

                    if(mp != null) {mp.stop();}
                }

            }

            // 드래그 시작 (트래킹) 하면 호출
            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                isTracking = true;

            }

            // 드래그 마치면 (드래킹 종료) 하면 호출
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                pos = seekBar.getProgress();

                if(mp != null) {mp.seekTo(pos);}

                isTracking = false;
            }
        });

        btnPlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // MediaPlayer 객체 초기화, 재생
                mp = MediaPlayer.create(
                        getApplicationContext(),    // 현재 화면의 제어권자
                        R.raw.chacha);  // 음악 파일 리소스
                mp.setLooping(false);   // true: 무한반복

                // 재생이 끝나면 호출되는 콜백 메소드
                mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                    @Override
                    public void onCompletion(MediaPlayer mp) {
                        Log.d("myapp", "연주종료 " + mp.getCurrentPosition() + " / " + mp.getDuration());
                        btnPlay.setVisibility(View.VISIBLE);
                        btnPause.setVisibility(View.INVISIBLE);
                        btnResume.setVisibility(View.INVISIBLE);
                        btnStop.setVisibility(View.INVISIBLE);
                    }
                });

                mp.start(); // 노래 재생 시작

                int duration = mp.getDuration();   // 음악의 재생시간 (ms)
                sb.setMax(duration);    // SeekBar 의 범위를 음악의 재생 시간으로 설정
                new MyThread().start(); // SeekBar 쓰레드 시작

                btnPlay.setVisibility(View.INVISIBLE);
                btnStop.setVisibility(View.VISIBLE);
                btnPause.setVisibility(View.VISIBLE);
            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 음악 종료
                pos = 0;

                if(mp != null) {
                    mp.stop();  // 재생 멈춤
                    mp.seekTo(0);   // 음악의 처음으로 이동
                    mp.release();   // 자원해제
                    mp = null;
                }

                sb.setProgress(0);  // SeekBar도 초기 위치로

                btnPlay.setVisibility(View.VISIBLE);
                btnPause.setVisibility(View.INVISIBLE);
                btnResume.setVisibility(View.INVISIBLE);
                btnStop.setVisibility(View.INVISIBLE);
            }
        });

        // 일시중지
        btnPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pos = mp.getCurrentPosition();  // 현재 재생중이던 위치 저장
                mp.pause(); // 일시 중지

                btnPause.setVisibility(View.INVISIBLE);
                btnResume.setVisibility(View.VISIBLE);
            }
        });

        // 멈춘 시점부터 재시작
        btnResume.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mp.seekTo(pos); // 일시 정지시 위치로 이동.
                mp.start(); // 재생 시작
                new MyThread().start(); // SeekBar 이동(쓰레드)

                btnResume.setVisibility(View.INVISIBLE);
                btnPause.setVisibility(View.VISIBLE);
            }
        });

    } // end onCreate()

    // onPause 단계에서 자원 해제하는 것이 좋다..!!

    @Override
    protected void onPause() {
        super.onPause();

        if(mp != null) {
            mp.release();   // 자원해제
        }

        btnPlay.setVisibility(View.VISIBLE);
        btnPause.setVisibility(View.INVISIBLE);
        btnResume.setVisibility(View.INVISIBLE);
        btnStop.setVisibility(View.INVISIBLE);
    } // end onPause()

} // end Activity

 

 

3. 앱은 사용자에 의해서만 종료할 수 있다(즉, 액티비티는 종료시킬 수 있으나 앱은 종료 불가!!)

 

 

4. AsyncTask
 : 안드로이드는 메인스레드를 멈출 수 없다

   [그렇기 때문에] 메인스레드를 멈추려 하면 에러 발생

** 동기 : 작업이 완료될 때까지 다음 작업을 실행하지 않는다.

 

** 비동기 : 작업이 시작되면 다음 작업을 진행하고, 작업이 완료된 경우 다시 호출이 된다.

[추가] onPreExecute( ), onProgressUpadate( ), onPostExecute( ) 메소드는 메인 스레드에서 실행되므로

         UI 객체에 자유롭게 접근할수 있다

 

 

5. a011_handler 모듈

1) MainActivity 액티비티, activity_main 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a011_handler">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Thread"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <TextView
        android:id="@+id/tvMainValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="메인스레드 값"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

    <TextView
        android:id="@+id/tvBackValue1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="작업스레드 값1"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/tvBackValue2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="작업스레드 값2"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btnIncrease"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick"
        android:text="메인스레드 값 증가"/>

    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick2"
        android:text="메인스레드 중지"/>

</LinearLayout>
package com.lec.android.a011_handler;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

/** Thread 사용
 *   Thread 클래스(run 함수의 구현 포함)를 별도로 정의하고,
 *   Thread의 객체를 메인 Activity내에서 생성하고 Thread를 start시킴.
 *
 *   일반적인 자바 프로그래밍에서는 메인스레드가 종료되면, 작업스레도도 잘(?) 종료되지만
 *   안드로이드 액티비티에선 메인스레드가 종료되도 (심지어 어플이 종료되도) 작업스레드가
 *   종료되지 않는 경우가 있습니다.  그래서 setDaemon(true) 메소드를 통해
 *   메인스레드와 종료동기화를 시킵니다.
 *
 *   ★ 작업 쓰레드는 Main UI 를 직접 접근할수 없다.★
 *   ★  안드로이드는 메인 스레드를 강제로 종료시킬수 없다. ★
 */

public class MainActivity extends AppCompatActivity {

    int mainValue = 0;
    int backValue1 = 0;
    int backValue2 = 0;
    TextView tvMainValue;
    TextView tvBackValue1;
    TextView tvBackValue2;

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

        tvMainValue = findViewById(R.id.tvMainValue);
        tvBackValue1 = findViewById(R.id.tvBackValue1);
        tvBackValue2 = findViewById(R.id.tvBackValue2);

        BackThread thread1 = new BackThread();
        thread1.setDaemon(true);    // ★★ [중요] 메인스레드와 종료 동기화..!!
                                    // 자바에서는 자동으로 동기화가 되지만 안드로이드에서는 수동으로 처리해줘야 함
                                    // 만약 안드로이드에서 메인스레드와 종료 동기화에 대한 처리를 하지 않으면
                                    // 메인쓰레드가 죽어도 작업쓰레드는 계속 일하는 상태가 되버린다..!!
        thread1.start();    // 작업스레드 시작

        BackRunnable runnable = new BackRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.setDaemon(true);
        thread2.start();

    } // onCreate()

    public void mOnClick(View v) {
        mainValue++;

        tvMainValue.setText("메인스레드 값 : " + mainValue);
        tvBackValue1.setText("작업스레드1 값 : " + backValue1);
        tvBackValue2.setText("작업스레드2 값 : " + backValue2);

    } // end mOnClick()

    // 안드로이드는 메인 쓰레드를 강제 종료시킬 수 없다.
    public void mOnClick2(View v) {
        Thread.currentThread().stop();  // Caused by: java.lang.UnsupportedOperationException
    } // end mOnClick2()

    // 1. Thread 를 상속받은 작업스레드
    class BackThread extends Thread {
        @Override
        public void run() {
            while (true) {
                backValue1++;   // 작업스레드 값 증가

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 작업쓰레드에서 메인 UI 직접 건드릴 수 없다.
                //tvBackValue1.setText("" + backValue1);  // android.view.ViewRootImpl$CalledFromWrongThreadException

            }
        }
    } // end class BackThread

    // 2. Runnable 을 implement
    class BackRunnable implements Runnable {

        @Override
        public void run() {
            while (true) {
                backValue2 += 2;   // 작업스레드 값 증가

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    } // end class BackRunnable

} // end Activity

 

2) Main2Activity 액티비티, activity_main2 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a011_handler">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".Main2Activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".MainActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Handler"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <TextView
        android:id="@+id/tvMainValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="메인스레드 값"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvBackValue1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="작업스레드 값1"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/tvBackValue2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="작업스레드 값2"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/tvBackValue3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="작업스레드 값3"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/tvBackValue4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="작업스레드 값4"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btnIncrease"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick"
        android:text="메인스레드 값 증가"/>
</LinearLayout>
package com.lec.android.a011_handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.TextView;

/** Handler
 *  자바는 자바가상머신 위에서 자체적으로 스레드를 생성하고 운영하긴 하지만,
 *  스레드 부분 만큼은 '운영체제'의 영향을 받는다.
 *  안드로이드에서 돌아가는 자바는 결국 '안드로이드 운영체제'의 영향을 받을수 밖에 없는데, ..
 *  안드로이드 운영체제의 경우 '작업스레드' 가 '메인스레드'의 변수를 참조하거나 변경을 할수 있어도,
 *  '메인스레드' 에서 정의된 UI 를 변경할수는 없게 하고 있습니다.  --> CalledFromWrongThreadException !! (이전 예제 참조)
 *
 *  안드로이드에서 '작업 스레드' 가 '메인스레드의 UI' 에 접근(변경/사용) 하려면 Handler 를 사용해야 합니다
 *  Handler 는 메인스레드와 작업스레드 간에 통신을 할 수 있는 방법입니다ㅣ
 *
 *  사용 방법:
 *      ▫ 'Handler 를 생성'한 스레드만이 다른 작업스레드가 전송하는 'Message' 나 'Runnable객체' 를 수신하는 기능을 할 수 있다.
 *      ▫  Message 전송은 sendMessage()
 *      ▫  Runnable 전송은 postXXX()
 */
public class Main2Activity extends AppCompatActivity {

    int mainValue = 0;
    int backValue1 = 0;
    int backValue2 = 0;
    TextView tvMainValue;
    TextView tvBackValue1, tvBackValue2, tvBackValue3, tvBackValue4;

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

        tvMainValue = findViewById(R.id.tvMainValue);
        tvBackValue1 = findViewById(R.id.tvBackValue1);
        tvBackValue2 = findViewById(R.id.tvBackValue2);
        tvBackValue3 = findViewById(R.id.tvBackValue3);
        tvBackValue4 = findViewById(R.id.tvBackValue4);

        // 방법1, 방법2
        // 스레드 생성하고 시작
        BackThread1 thread1 = new BackThread1();
        thread1.setDaemon(true);
        thread1.start();

        // 방법3
        BackThread3 thread3 = new BackThread3(handler3);  // 메인스레드의 Handler 객체를 외부 클래스에 넘겨줌
        thread3.setDaemon(true);
        thread3.start();


        // 방법4
        BackThread4 thread4 = new BackThread4(handler4);  // 메인스레드의 Handler 객체를 외부 클래스에 넘겨줌
        thread4.setDaemon(true);
        thread4.start();




    } // end onCreate()


    public void mOnClick(View v){
        mainValue++;
        tvMainValue.setText("MainValue:" + mainValue);
    }

    class BackThread1 extends Thread{
        @Override
        public void run() {
            while(true){
                // 방법1) 메인에서 생성된 Handler 객체의 sendEmptyMessage 를 통해 Message 전달
                backValue1++;
                handler1.sendEmptyMessage(1);

                // 방법2) 메인에서 생성된 Handler 객체의 postXXX() 를 통해 Runnable 객체 전달
                backValue2 += 2;
                handler2.post(new Runnable() {
                    @Override
                    public void run() {  // Runnable 의 run() 에서 메인 UI 접근
                        tvBackValue2.setText("BackValue2: " + backValue2);
                    }
                });

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } // end while
        } // end run()
    } // end BackThread1

    //---------------------------------------------------------------------------------------------
    // 방법1
    // '메인스레드' 에서 Handler 객체를 생성한다.
    // Handler 객체를 생성한 스레드 만이 다른 스레드가 전송하는 Message나 Runnable 객체를
    // 수신할수 있다.
    // 아래 생성된 Handler 객체는 handleMessage() 를 오버라이딩 하여
    // Message 를 수신합니다.
    Handler handler1 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            if(msg.what == 1){ // Message id 가 1 이면
                tvBackValue1.setText("BackValue1:" + backValue1);  // 메인스레드의 UI 변경
            }
        }
    };

    //----------------------------------------------------------------------
    // 방법2
    Handler handler2 = new Handler();


    // 방법3
    Handler handler3 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            if(msg.what == 0){
                // 메세지를 통해 받은 값을 메인 UI 에 출력
                tvBackValue3.setText("BackValue3:" + msg.arg1);
            }
        }
    };


    // 방법4
    Handler handler4 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            if(msg.what == 0){
                tvBackValue4.setText("BackValue4: " + msg.arg1);
            }
        }
    };



} // and Activity


// #3
// 작업스레드가 메인스레드와 완전히 분리되어 있어서 메인스레드에서 생성한 핸들러를 작업스레드에서
// 직접 참조 할수 없을때, Message 생성자 함수로 메세지를 생성하여 보내주면 됩니다.
// 가령 아래와 같이 메인스레드의 핸들러를 직접 사용할수 없는 분리된 작업 스레드
class BackThread3 extends Thread{

    int backValue = 0;
    Handler handler;

    BackThread3(Handler handler) {this.handler = handler;}

    @Override
    public void run() {
        while(true){
            backValue += 3;

            Message msg = new Message(); //메세지 생성
            msg.what = 0;  // 메세지 id
            msg.arg1 = backValue;

            handler.sendMessage(msg);   // 메인스레드의 핸들러에 메세지 보내기

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } // end run()

} // end BackThread3


// 방법4 : 메인스레드의 Handler 를 직접 사용할수 없는 분리된 작업 스레드
class BackThread4 extends Thread{
    int backValue = 0;
    Handler handler;

    BackThread4(Handler handler) {this.handler = handler;}

    @Override
    public void run() {
        while(true){
            backValue += 4;

            // obtain 메소드로 메세지 생성
            // obtain(Handler h, int what, int arg1, int arg2)
            // Message.obtain(..) ← 다양하게 오버로딩 되어 있슴
            Message msg = Message.obtain(handler, 0, backValue, 0);
            handler.sendMessage(msg);   // 메인스레드의 Handler 에 메세지 보내기

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    } // end run()
    
} // end BackThread4

 

3) Main3Activity 액티비티, activity_main3 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a011_handler">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <activity android:name=".Main3Activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".Main2Activity" />
        <activity android:name=".MainActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="스케쥴링"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <TextView
        android:id="@+id/tvSummary"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="업로드를 시작하려면 다음 버튼을 누르세요."
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <Button
        android:id="@+id/btnUpload1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick1"
        android:text="UPLOAD1" />

    <Button
        android:id="@+id/btnUpload2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick2"
        android:text="UPLOAD2" />

    <Button
        android:id="@+id/btnUpload3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick3"
        android:text="UPLOAD3" />

    <Button
        android:id="@+id/btnUpload4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick4"
        android:text="UPLOAD4" />
</LinearLayout>
package com.lec.android.a011_handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.util.Timer;
import java.util.TimerTask;

/**
 * • 작업 스케쥴링:
 *   ▫ 작업스레드의 실행 시점을 조절하여, 작업 로드가 많은 작업을 나중으로 미룸으로써
 *     응용프로그램이 '끊김'없이 실행될수 있도록할수 있다.
 *
 *  •  Handler 사용한 구현 방법:
 *        boolean sendMessageAtTime (Message msg, uptimeMillis)
 *        boolean sendEmptyMessageAtTime (what, uptimeMillis)
 *        boolean sendMessageDelayed (Message msg, long delayMillis)
 *        boolean sendEmptyMessageDelayed (what, long delayMillis)
 *        boolean postAtTime (Runnable r, uptimeMillis)
 *        boolean postDelayed (Runnable r, long delayMillis)
 *        boolean postAtFrontOfQueue(Runnable r)
 *
 *        xxxAtFrontOfQueue – 큐의 가장 앞에 메세지를 삽입합니다.
 *        xxxAtTime – 지정한 시간으로 설정하여 큐에 삽입합니다.
 *        xxxDelayed – 현재시간으로부터 지정한 시간만큼 뒤로 설정하여 큐에 삽입합니다.
 *
 *  •  java.util.Timer, java.util.TimerTask 사용한 구현 방법:
 *
 */

public class Main3Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
    } // end onCreate()

    Handler mHandler = new Handler() {

        @Override
        public void handleMessage(@NonNull Message msg) {
            doUpload(msg.what);
        }
    };

    void doUpload(int n) {
        Toast.makeText(getApplicationContext(), n + " : 업로드를 완료했습니다.", Toast.LENGTH_LONG).show();
    }

    // #1. 메인스레드가 메인스레드 자신에게 메시지 보내기
    // sendEmptyMessageDelayed()
    public void mOnClick1(View v){
        new AlertDialog.Builder(this)
                .setTitle("질문1")
                .setMessage("업로드 하시겠습니까?")
                .setPositiveButton("예", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mHandler.sendEmptyMessageDelayed(1, 3000);
                    }
                })
                .setNegativeButton("아니요", null)
                .show();
    }

    /*  예제#2. Handler 로 Runnable 을 지연(delay)하여 보냄(post)
        메인스레드가 메인스레드 자신에게 Runnable 을 보내는 경우임
        postDelayed(Runnable) 사용
    */
    public void mOnClick2(View v) {
        new AlertDialog.Builder(this)
                .setTitle("질문2")
                .setMessage("업로드 하시겠습니까?")
                .setPositiveButton("예", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                doUpload(2);
                            }
                        }, 3000);
                    }
                })
                .setNegativeButton("아니요", null)
                .show();
    }

    // #3. View 에 Runnable 을 담아서 보냄
    public void mOnClick3(View v) {
        new AlertDialog.Builder(this)
                .setTitle("질문3")
                .setMessage("업로드 하시겠습니까?")
                .setPositiveButton("예", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // View (Button)을 통해서도 Runnable 을 생성해서 보낼 수 있다.
                        Button btnUpload = findViewById(R.id.btnUpload3);
                        btnUpload.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                doUpload(3);
                            }
                        }, 3000);
                    }
                })
                .setNegativeButton("아니요", null)
                .show();
    }

    // #4. Timer, TimerTask 사용
    public void mOnClick4(View v) {
        new AlertDialog.Builder(this)
                .setTitle("질문4")
                .setMessage("업로드 하시겠습니까?")
                .setPositiveButton("예", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {

                        Timer timer = new Timer();
                        TimerTask task = new TimerTask() {
                            @Override
                            public void run() {
                                // 예약할 작업 내용 기술
                                mHandler.sendEmptyMessage(4);
                            }
                        };
                        timer.schedule(task, 3000);
                    }
                })
                .setNegativeButton("아니요", null)
                .show();
    }

} // end Activity

 

4) Main4Activity 액티비티, activity_main4 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a011_handler">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".Main4Activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".Main3Activity" />
        <activity android:name=".Main2Activity" />
        <activity android:name=".MainActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Timer"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <TextView
        android:id="@+id/tvResult1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="결과창1"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvResult2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="결과창2"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvResult3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="결과창3"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvResult4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="결과창4"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvResult5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="결과창5"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
package com.lec.android.a011_handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import android.widget.Toast;

// TODO
// Value1
// 1 ~ 10 까지 1초 단위로 증가시키기
// 10초에 도달하면 멈춰서 Toast 띄우기
// Message 사용

// Value2
// 1 ~ 20 까지 1초 단위로 증가시키기
// 20초에 도달하면 멈춰서 Toast 띄우기
// Handler 사용
public class Main4Activity extends AppCompatActivity {

    int value1 = 0, value2 = 0, value3 = 0, value4 = 0, value5 = 0;
    TextView tvResult1, tvResult2, tvResult3, tvResult4, tvResult5;
    Handler mHandler2, mHandler3;

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

        tvResult1 = findViewById(R.id.tvResult1);
        tvResult2 = findViewById(R.id.tvResult2);
        tvResult3 = findViewById(R.id.tvResult3);
        tvResult4 = findViewById(R.id.tvResult4);
        tvResult5 = findViewById(R.id.tvResult5);

        // 방법 #1 핸들러 객체를 외부에서 생성
        mHandler1.sendEmptyMessage(0); // 앱 시작과 동시에 핸들러에 메세지 전달

        // 방법 #2 handler.postDelayed() 사용
        mHandler2 = new Handler();
        mHandler2.postDelayed(new Runnable() {
            @Override
            public void run() {
                value2++;
                tvResult2.setText("Value2 = " + value2);
                if(value2 < 20){
                    mHandler2.postDelayed(this, 1000);
                } else {
                    Toast.makeText(getApplicationContext(), "Value2 종료", Toast.LENGTH_LONG).show();
                }
            }
        }, 0);

        // 방법 #3 메소드 내부에서 생성
        mHandler3 = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                value3++;
                tvResult3.setText("Value3 = " + value3);

                if(value3 < 5){
                    mHandler3.sendEmptyMessageDelayed(0, 1000);
                } else {
                    Toast.makeText(getApplicationContext(), "Value3 종료", Toast.LENGTH_LONG).show();
                }
            }
        };
        mHandler3.sendEmptyMessage(0);  // 시작!

        // 방법 #3
        // 핸들러를 사용하지 않고도 일정시간마다 (혹은 후에) 코스를 수행할수 있도록
        // CountDownTimer 클래스가 제공된다.
        // '총시간'  과 '인터벌(간격)' 을 주면 매 간격마다 onTick 메소드를 수행한다.
        new CountDownTimer(15 * 1000, 1000) {

            @Override
            public void onTick(long millisUntilFinished) {  // 매 간격마다 수행하는 코드
                value4++;
                tvResult4.setText("Value4 = " + value4);
            }

            @Override
            public void onFinish() {    // 종료시 수행하는 코드
                Toast.makeText(getApplicationContext(), "Value4 종료", Toast.LENGTH_LONG). show();
            }
        }.start();  // 타이머 시작


    } // end onCreate


    Handler mHandler1 = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            value1++;

            tvResult1.setText("Value1 = " + value1);

            if(value1 < 10){
                // 메세지를 처리하고 또다시 핸들러에 메세지 전달 (1000ms 지연)
                mHandler1.sendEmptyMessageDelayed(0,1000);
                // 첫번째 매개변수는 message 값
                // 두번째 매개변수는 millisec
            } else {
                Toast.makeText(getApplicationContext(), "Value1 종료", Toast.LENGTH_LONG).show();
            }
        }
    };

} // end Activity

 

5) Main5Activity 액티비티, activity_main5 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a011_handler">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".Main5Activity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".Main4Activity" />
        <activity android:name=".Main3Activity" />
        <activity android:name=".Main2Activity" />
        <activity android:name=".MainActivity" />
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="AsyncTask"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <TextView
        android:id="@+id/tvMainValue"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="메인스레드 값"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvBackValue1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="작업스레드 값1"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btnIncrease"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="mOnClick"
        android:text="메인스레드 값 증가"/>
</LinearLayout>
package com.lec.android.a011_handler;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

/** AsyncTask
 *    백그라운드(background) 작업 스레드 수행,
 *    좀더 쉽게 스레드 만들고 운영,
 *    작업스레드 결과값까지 쉽게 받아볼수 있다.
 *    심지어 Handler 없이도 메인 UI 접근 할수 있다!
 *
 *    AsyncTask 의 메소드
 *      onPreExecute() : 백그라운드 작업 시작하기 전에 호출
 *      doInBackground() : 백그라운드 작업, 시간이 많이 걸리는 '통신' 작업이나 복잡한 연산 작업등을 (비동기로)수행케 해야 한다.
 *      onProgressUpdate() : 백그라운즈 작업 도중 (여러번) 호출가능, 중간중간에 UI업데이트시 사용 가능!
 *      onPostExecute() : doInBackground() 완료되면 호출
 *
 *    AsyncTask<Params, Progress, Result>
 *      Params: doItBackground 에서 사용할 변수 타입
 *      Progress: onProgress 에서 사용할 변수의 타입
 *      Result : onPostExecute 에서 사용할 변수의 타입
 *
 */

public class Main5Activity extends AppCompatActivity {

    int mainValue = 0;
    int backValue1 = 0;
    int backValue2 = 0;
    TextView tvMainValue;
    TextView tvBackValue1, tvBackValue2;

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

        tvMainValue = findViewById(R.id.tvMainValue);
        tvBackValue1 = findViewById(R.id.tvBackValue1);
        tvBackValue2 = findViewById(R.id.tvBackValue2);

        Log.d("myapp", "PRE!!");
        BackgroundTask backgroundTask = new BackgroundTask();
        backgroundTask.execute(100);

        Log.d("myapp", "post!!");
    } // end onCreate()

    // [장점] Handler 없이도 메인 UI에 접근 가능..!!
    // AsyncTask<Params, Progress, Result>
    //      Params: doItBackground 에서 사용할 변수 타입
    //      Progress: onProgress 에서 사용할 변수의 타입
    //      Result : onPostExecute 에서 사용할 변수의 타입
    class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {

        // 백그라운드 작업 시작하기 전에 호출
        @Override
        protected void onPreExecute() {
            Log.d("myapp", "onPreExecute");
            super.onPreExecute();
        }

        // [이게 본론...!!] 백그라운드 작업. 반드시 구현!
        @Override
        protected Integer doInBackground(Integer... integers) { // 가변매개변수, integers 는 Integer[]

            for(backValue1 = 0; backValue1 < integers[0]; backValue1++) {
                if(backValue1 % 10 == 0) {
                    publishProgress(backValue1);    // progress 상태를 update 뽑아냄 --> onProgressUpdate 호출되고 매개변수값 전달됨.
                }

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            return backValue1;  // onPostExecute 에 넘어가는 값

            // ※ doInBackground() 에서 시간이 많이 걸리는 '통신' 작업이나 복잡한 연산 작업등을 (비동기로)수행케 해야 한다.

        }

        // 백그라운드 작업 도중 (여러번) 호출 가능, 진행 상황 업데이트, 중간 중간 UI 업데이트시 사용
        @Override
        protected void onProgressUpdate(Integer... values) {
            Log.d("myapp", "Progress : " + values[0] + "%");    // publishProgress(i) 가 보낸 값
            super.onProgressUpdate(values);

            tvBackValue1.setText("onProgressUpdate : " + values[0]);    // ★ Handler 없이도 메인 UI 접근 가능!
        }

        // doInBackground() 완료되면 호출
        @Override
        protected void onPostExecute(Integer integer) { // doInBackGround 에서 return 한 값을 매개변수로 받는다.
            Log.d("myapp", "Result : " + integer);
            super.onPostExecute(integer);

            tvBackValue1.setText("onPostExecute: " + integer);  // ★ Handler 없이도 메인 UI 접근 가능!
        }

    } // end AsyncTask

    public void mOnClick(View v) {
        mainValue++;
        tvMainValue.setText("MainValue : " + mainValue);
    }

} // end Activity

 

 

6. a012_Vibrator 모듈

1) MainActivity 액티비티, activity_main 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a012_vibrator">

    <!-- 진동 기능 사용권한 획득 -->
    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Vibrator"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <Button
        android:id="@+id/btnVib1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진동1" />

    <Button
        android:id="@+id/btnVib2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진동2" />

    <Button
        android:id="@+id/btnVib3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진동3" />

    <Button
        android:id="@+id/btnVib4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="진동4" />

</LinearLayout>
package com.lec.android.a012_vibrator;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.View;
import android.widget.Button;

// 진동
// 1. 진동 권한을 획득해야한다. AndroidManifest.xml
//  manifest에서 진동 기능 사용권한 획득하기
// 2. Vibrator 객체를 얻어서 진동시킨다

public class MainActivity extends AppCompatActivity {

    Button btnVib1, btnVib2, btnVib3, btnVib4;

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

        btnVib1 = findViewById(R.id.btnVib1);
        btnVib2 = findViewById(R.id.btnVib2);
        btnVib3 = findViewById(R.id.btnVib3);
        btnVib4 = findViewById(R.id.btnVib4);

        final Vibrator vibrator
                = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);

        btnVib1.setText("5초진동");
        btnVib1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                vibrator.vibrate(5000); // 지정시간동안 진동
            }
        });

        btnVib2.setText("지정한 패턴으로 진동");
        btnVib2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                long[] pattern = {100, 300, 100, 700, 300, 2000};   // 단위 ms
                // 대기, 진동, 대기, 진동, ...
                // 짝수인덱스 : 대기
                // 홀수인덱스 : 진동
                vibrator.vibrate(pattern,   // 진동패턴(배열)
                        -1);        // 반복
                // 0: 무한반복, -1 : 반복없음
                // 양의 정수 : 진동 패턴 배열의 해당 인덱스부터 진동 무한 반복.
            }
        });

        btnVib3.setText("무한반복으로 진동");
        btnVib3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                vibrator.vibrate(
                        new long[] {100, 1000, 100, 500, 100, 500, 100, 1000}
                        , 0);
            }
        });

        btnVib4.setText("진동 취소");
        btnVib4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                vibrator.cancel();  // 진동 취소
            }
        });
    }
}

 

 

7. a014_dialog 모뎀

1) MainActivity 액티비티, activity_main, dialog_layout11, dialog_layout12 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lec.android.a014_dialog">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

** activity_main 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Dialog"
        android:textAppearance="@style/TextAppearance.AppCompat.Display1" />

    <Button
        android:id="@+id/btnDialog1"
        android:layout_width="match_parent"
        android:layout_height="72dp"
        android:onClick="showDialog1"
        android:text="대화상자 1"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <Button
        android:id="@+id/btnDialog2"
        android:layout_width="match_parent"
        android:layout_height="72dp"
        android:onClick="showDialog2"
        android:text="대화상자 2"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />

    <TextView
        android:id="@+id/tvResult"
        android:layout_width="match_parent"
        android:layout_height="52dp"
        android:text="결과창"
        android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>

** dialog_layout11 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="400dp"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <!-- 대화상자 레이아웃의 경우
    layout_width 를 match_parent 로 하면
    너무 좁게 나온다. (함 보자) 적절한 크기로 지정해주는것이 좋다 -->

    <ImageView
        android:id="@+id/ivDlgBanner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/tvDlgTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="이것은 다이얼로그입니다"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/etDlgInput"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" >
        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/btnDlgEvent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="이벤트 처리 해볼께요" />

    <RadioGroup
        android:id="@+id/radioGroup1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" >

        <RadioButton
            android:id="@+id/radio0"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:checked="true"
            android:text="RadioButton0" />

        <RadioButton
            android:id="@+id/radio1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="RadioButton1" />

        <RadioButton
            android:id="@+id/radio2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="RadioButton2" />
    </RadioGroup>

</LinearLayout>

** dialog_layout12 레이아웃

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <ImageView
        android:id="@+id/ivPhoto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:src="@mipmap/ic_launcher" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:text="이름을 입력하세요"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/etName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/ivPhoto"
        android:layout_alignParentRight="true"
        android:ems="10"
        android:inputType="text" >

        <requestFocus />
    </EditText>

    <Button
        android:id="@+id/btnOk"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/ivPhoto"
        android:text="확인" />

    <Button
        android:id="@+id/btnCancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/btnOk"
        android:layout_alignBottom="@+id/btnOk"
        android:layout_alignParentRight="true"
        android:text="취소" />

</RelativeLayout>

** MainActivity 액티비티

package com.lec.android.a014_dialog;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    // 대화상자 객체
    Dialog dlg1;
    ImageView ivDlgBanner;
    Button btnDlgEvent;

    Dialog dlg2;
    TextView tvResult;


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

        tvResult = findViewById(R.id.tvResult);

        // Dialog 클래스로 다이얼로그 객체 생성및 세팅
        dlg1 = new Dialog(this);   // 다이얼로그 객체 생성
        dlg1.setContentView(R.layout.dialog_layout11);   // 다이얼로그 화면 등록

        // Dialog 안의 View 객체들 얻어오기
        ivDlgBanner = dlg1.findViewById(R.id.ivDlgBanner);
        btnDlgEvent = dlg1.findViewById(R.id.btnDlgEvent);

        btnDlgEvent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ivDlgBanner.setImageResource(R.drawable.face04);
                Toast.makeText(getApplicationContext(), "다이얼로그 버튼을 눌렀어요", Toast.LENGTH_SHORT).show();
            }
        });

        // Activity 에 Dialog 등록하기
        dlg1.setOwnerActivity(MainActivity.this);
        dlg1.setCanceledOnTouchOutside(true);   // 다이얼로그 바깥 영역 클릭시 (혹은 back 버튼 클릭시) hide() 상태가 됨.
                        // 종료할 것인지 여부,  true : 종료됨,  false : 종료 안됨.

        // #2
        dlg2 = new Dialog(this);
        dlg2.setContentView(R.layout.dialog_layout12);
        dlg2.setOwnerActivity(MainActivity.this);
        dlg2.setCanceledOnTouchOutside(false);

        final EditText etName = dlg2.findViewById(R.id.etName);
        Button btnOk = dlg2.findViewById(R.id.btnOk);
        Button btnCancel = dlg2.findViewById(R.id.btnCancel);

        btnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String str = etName.getText().toString();
                tvResult.setText(str);
                dlg2.dismiss();   // 다이얼로그 객체 제거
            }
        });

        btnCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dlg2.dismiss();
            }
        });

        // 다이얼로그가 등장할때 호출되는 메소드
        dlg2.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                etName.setText("");
            }
        });


    }// end onCreate()

    public void showDialog1(View v){
        dlg1.show();   // 다이얼로그 띄우기
    }

    public void showDialog2(View v){
        dlg2.show();
    }

} // end Activity

'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글

2020.04.22  (0) 2020.04.22
2020.04.21  (0) 2020.04.21
2020.04.17  (0) 2020.04.17
2020.04.16  (0) 2020.04.16
2020.04.14  (0) 2020.04.14