본문 바로가기
Android

fragment

by noddu 2021. 4. 6.
728x90
반응형

 

환경설정탭과 Web탭 만들기

 

 

 

BottomNavigationView 만들기

 

 

 

 

 

 

MENU 바 만들기

 

menu 커스텀은 menu.xml에서 해야함

 

 

 

다시 Main으로와서 menu 선택하고

labelVisibilityMode를 labeled로 바꾸면 menu에 설정한 item들이 다 나온다

 

 

 

 

 

 

4개의 메뉴에 들어갈 fragment 4개 만들기

 

 

그리고fragment의 레이아웃을 constraintLayout으로 바꿔준다

 

 

 

●MainActivity.java

public class MainActivity extends AppCompatActivity {
    // 1. 어떤 메뉴를 선택했는지 판별하기
    BottomNavigationView nv;
    // 2. 선택한 메뉴에 따라서 FrameLayout에 들어갈 Fragment를 갈아끼우기
    // 2-1 각각 fragment들 객체 생성
    Fragment_1 fg1;
    Fragment_2 fg2;
    Fragment_3 fg3;
    Fragment_4 fg4;





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

        fg1 = new Fragment_1();
        fg2 = new Fragment_2();
        fg3 = new Fragment_3();
        fg4 = new Fragment_4();

        // 2-2 최초 실행될 fragment 고르기
        // 1) fragment가 들어갈 공간(frameLayout)의 id
        // 2) 같이 끼울 Fragment 객체
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fg1).commit();

        nv = findViewById(R.id.bottomNavigationView);

        // 3. navigation 에서 선택한 메뉴에 따라 fragment 바꿔끼우기
        nv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                switch (item.getItemId()){
                    case R.id.tab1:
                        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fg1).commit();
                        Toast.makeText(getApplicationContext(),"Web 선택!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.tab2:
                        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fg2).commit();
                        Toast.makeText(getApplicationContext(),"Stopwatch 선택!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.tab3:
                        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fg3).commit();
                        Toast.makeText(getApplicationContext(),"Kill 선택!",Toast.LENGTH_SHORT).show();
                        break;
                    case R.id.tab4:
                        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fg4).commit();
                        Toast.makeText(getApplicationContext(),"Setting 선택!",Toast.LENGTH_SHORT).show();
                        break;
                }

                // return false => 아직 현재 이벤트가 끝나지 않았다 라는 뜻
                return true;
            }
        });
    }
}

각각 만들기

 

 

 

                                                      ▼ findViewById 하는법

 

 

 

fragment_4.java

public class Fragment_4 extends Fragment {
    // Fragmant 4
    // 1. Fragment 상황에서 findViewById하기  Toast띄우기
    // 2. Fragment 1로 데이터 넘기기
    // 2-1 App 데이터(캐시)로 저장하기 -> SharedPreference => 간단한데이터저장 (로그인정보,환경설정,첫 실행인지 감지)
    Button btn_setting;
    EditText edt_address;
    SharedPreferences spf;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_4, container, false);

        // MODE_PRIVATE -> mySFP라는 이름의 프리퍼런스가 없으면 새로 생성, 있으면 있는거 가져와라
        spf = getActivity().getSharedPreferences("mySPF", Context.MODE_PRIVATE);

        // 값 반영할 수 있는 객체
        SharedPreferences.Editor editor = spf.edit();

        btn_setting = view.findViewById(R.id.btn_setting);
        edt_address = view.findViewById(R.id.edt_address);

        btn_setting.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Toast 띄울 때 getApplicationContext() -> Fragment에서는 x
                // 앞에 getActivity(). 를 붙여주면 현재 Fragment가 포함된 Activity정보를 가지고 올 수 있음

                // 현재 url에 https:// 로 시작하지 않는다면 https:// 붙여주기
                String url = edt_address.getText().toString();
                if (!url.startsWith("https://")){
                    url = "https://"+url;
                }

                Toast.makeText(getActivity().getApplicationContext(), url, Toast.LENGTH_SHORT).show();

                //spf에 값 저장하기, 해당값을 App을 껐다 켜도 유지됨
                editor.putString("address",url);
                editor.commit();
            }
        });
        return view;
    }
}

url 주소 작성하고 버튼 누르면 ▼fragment_1.xml 에 브라우저 띄우기

 

●fragment_1.java

// onCreateView 빼고 나머지 지우기

public class Fragment_1 extends Fragment {
    // Fragmet 1
    // 1. WebView 쓰는 법
    // 2. 저장된 url 정보 가져오는 법 -> SPF
    WebView wv;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // fragment.xml 파일을 view로 inflate(id -> view)하여 반환

        View view = inflater.inflate(R.layout.fragment_1, container, false);
        wv = view.findViewById(R.id.webview);

        //spf에서 값 꺼내기, 한줄로 간단하게

        //String url = "https://www.smhrd.or.kr";
        String url = getActivity().getSharedPreferences("mySPF", Context.MODE_PRIVATE)
                .getString("address",null);

        //1-1 webView 환경설정해주기
        WebSettings settings = wv.getSettings();
        settings.setJavaScriptEnabled(true);    // javascript 사용가능

        //1-2 webClient 지정
        wv.setWebViewClient(new WebViewClient());

        //1-3 띄워줄 url 지정
        wv.loadUrl(url);

        //1-4 인터넷 권한 부여  ->  manifest.xml


        return view;
    }
}

 

<- manifest.xml 에서

인터넷 권한부여 하기

 

 

 

 

 

 

 

 

 

 

 

 

 

결과 

fragment_4

youtube.com 작성후 설정버튼 -> Web탭▼ 

 

fragment_1

 

 


Stopwatch 탭에 스톱워치 만들기

 

 

 

Process  -  컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램 (작업)

Thread  -  프로세스 병렬처리 - 하나의 프로그램 내에서 둘 이상의 작업을 동시에 진행하는 프로세스보다 작은단위

Handler  -  

 

 

 

 

작업 스케쥴링

시분할 시스템 ( 시간 분할 )

 

 

 

 

START!! 버튼 누르면 1초에 1씩 카운트되게 ( 각각 다른 스톱워치 - 스레드로 동작하게 )

 

 

 

 

 

 

 

 

SubThread가 MainThread의 작업Queue에 setText를 Message로 보냄

 

 

꺼낼때는 msg에서 arg1에 저장된 값을 setText

 

 

 

 

 

 

 

●fragment_2.java

public class Fragment_2 extends Fragment {
    // Thread 만들기
    // 1. Thread 클래스(설계도) 정의하기 (Thread 상속)
    // 2. run 메소드(Thread 시작시켰을 때 호출되는 메소드)를 오버라이딩 하기
    // 3. setText(UI업데이트)하기 위해 Handler 생성
    // 4. Message 전송 (sendMassage)하고 Handler에서 값 전달받음 (HandlerMessage)
    // 5. Handler에서 setText처리


    TextView tv1, tv2;
    Button btn1, btn2;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // tv1,tv2,btn1,btn2 찾아오기
        View view = inflater.inflate(R.layout.fragment_2, container, false);

        tv1 = view.findViewById(R.id.tv1);
        tv2 = view.findViewById(R.id.tv2);
        btn1 = view.findViewById(R.id.btn1);
        btn2 = view.findViewById(R.id.btn2);

        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Thread 실행시키기
                // 1) Thread를 상속받아 만들어진 클래스의 객체를 생성
                cntThread thread = new cntThread();

                // 2) start메소드를 호출한다.
                thread.start();
            }
        });

        return view;
    }

    // Handler 만들기
    Handler handler = new Handler() {
        // 메세지 수신 시 호출되는 메소드 생성 - 원래는 상속받는게 정석


        @Override
        public void handleMessage(@NonNull Message msg) {
            // msg에서 arg1에 저장된 값을 setText
            tv1.setText(msg.arg1 + "");  // setText 할 때 정수형을 넣으면 에러!! (정수형을 참조할 주소값으로 인식함)
        }
    };


    class cntThread extends Thread {
        @Override
        public void run() { //Thread 시작시켰을 때
            // Thread가 할 일을 적어주면 됨 - 1~10까지 1초씩 세는 코드

            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    // Thread가 CPU 사용하다가 양보 -> yield
                    // CPU 뺏어옴 -> interrupt
                    // 쉬는동안 (sleep) 다른 Thread가 interrupt를 걸었을 때를 대비
                }

                // i -> TextView에 setText만 하면 됨

                // 여기 (SubThread -> 내가 만든 Thread)에서는 setText등 UI업데이트 못함
                // Handler에 메세지를 전송해서 setText작업을 처리해야함

                Message msg = new Message();
                // Message 객체에는 변수가 3개있음 ▼
                // Object obj , int arg1 , int arg2  -  변수에 각각 값을 저장할 수 있음

                // i값을 handler에 보내서 textView에 setText 하기
                msg.arg1 = i;
                handler.sendMessage(msg);
            }
        }
    }
}

다음과 같이 하면

클릭 할때마다 Thread가 시작돼서 여러번누르면 스톱워치가 한번에 여러번 시작되므로

 

 

 

cntThread thread = null; 이라고 전역 변수로 지정하고

 

btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 버튼에 적혀있는 글자가 START 일때만 Thread 생성, 아니면 멈추게
                if (btn1.getText().toString().equals("START!!")){
                    thread = new cntThread();
                    thread.start();
                    // start 버튼을 누르면 버튼에 적힌 글자가 stop으로 바뀌게
                    btn1.setText("STOP!");
                }else{
                    thread.interrupt();     // 멈추는 법 (지금 실행하고 있는 쓰레드 멈춤)
                    // cpu 뺏기
                    // thread.interrupt 하면 thread 내부에서는 interrupt Exception 이 발생
                    // 멈추게 하려면 interruptException을 처리할 수 있는 catch 문에서 run 메소드를 끝내면 됨
                    btn1.setText("START!");
                }
            }
        });

START버튼 누를때에만 쓰레드 생성되게한다 ( 한번누르면 버튼이 STOP으로 바뀜 )

 

 

 

class cntThread extends Thread {
        @Override
        public void run() { //Thread 시작시켰을 때
            // Thread가 할 일을 적어주면 됨 - 1~10까지 1초씩 세는 코드

            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    return;
                    // 메소드는 return문을 만나면 종료되기 때문에
                    // 누군가가 이 Thread를 종료시키고 싶다면 interrupt를 걸어주면 됨

run 메소드에는 catch에 return; 해준다

 

 

 

 

 

tv2와 2번째 버튼도 활성화 하기 위해

 

만들어 준다

 

 

 

MainThread -> cntThread

 

지금 생성되는 Thread가 필요한 TextView를 매개변수로 보내줘야함 ( tv1 )

cntThread에서 매개변수로 TextView를 전달받는 생성자 생성하기

 

 

 

 

 

Message msg = new Message();
                // Message 객체에는 변수가 3개있음 ▼
                // Object obj , int arg1 , int arg2  -  변수에 각각 값을 저장할 수 있음

                // i값을 handler에 보내서 textView에 setText 하기
                msg.arg1 = i;
                msg.obj = textView;
                handler.sendMessage(msg);

Message에 textView를 담을 msg.obj를 만들고

 

 

 

cntThread -> handler

 

msg.obj 를 TextView로 다운캐스팅하고 그걸 setText

 

 

●fragment_2.java

public class Fragment_2 extends Fragment {
    // Thread 만들기
    // 1. Thread 클래스(설계도) 정의하기 (Thread 상속)
    // 2. run 메소드(Thread 시작시켰을 때 호출되는 메소드)를 오버라이딩 하기
    // 3. setText(UI업데이트)하기 위해 Handler 생성
    // 4. Message 전송 (sendMassage)하고 Handler에서 값 전달받음 (HandlerMessage)
    // 5. Handler에서 setText처리


    TextView tv1, tv2;
    Button btn1, btn2;
    cntThread thread = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        // tv1,tv2,btn1,btn2 찾아오기
        View view = inflater.inflate(R.layout.fragment_2, container, false);

        tv1 = view.findViewById(R.id.tv1);
        tv2 = view.findViewById(R.id.tv2);
        btn1 = view.findViewById(R.id.btn1);
        btn2 = view.findViewById(R.id.btn2);


        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 버튼에 적혀있는 글자가 START 일때만 Thread 생성, 아니면 멈추게
                if (btn1.getText().toString().equals("START!!")){
                    thread = new cntThread(tv1);   // 지금 생성되는 Thread가 필요한 TextView를 보내줘야함(매개변수)
                    thread.start();
                    // start 버튼을 누르면 버튼에 적힌 글자가 stop으로 바뀌게
                    btn1.setText("STOP!");
                }else{
                    thread.interrupt();     // 멈추는 법 (지금 실행하고 있는 쓰레드 멈춤)
                    // cpu 뺏기
                    // thread.interrupt 하면 thread 내부에서는 interrupt Exception 이 발생
                    // 멈추게 하려면 interruptException을 처리할 수 있는 catch 문에서 run 메소드를 끝내면 됨
                    btn1.setText("START!");
                }
            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // 버튼에 적혀있는 글자가 START 일때만 Thread 생성, 아니면 멈추게
                if (btn2.getText().toString().equals("START!!")){
                    thread = new cntThread(tv2);   // 지금 생성되는 Thread가 필요한 TextView를 보내줘야함(매개변수)
                    thread.start();
                    // start 버튼을 누르면 버튼에 적힌 글자가 stop으로 바뀌게
                    btn2.setText("STOP!");
                }else{
                    thread.interrupt();     // 멈추는 법 (지금 실행하고 있는 쓰레드 멈춤)
                    // cpu 뺏기
                    // thread.interrupt 하면 thread 내부에서는 interrupt Exception 이 발생
                    // 멈추게 하려면 interruptException을 처리할 수 있는 catch 문에서 run 메소드를 끝내면 됨
                    btn2.setText("START!");
                }
            }
        });

        return view;
    }

    // Handler 만들기
    Handler handler = new Handler() {
        // 메세지 수신 시 호출되는 메소드 생성 - 원래는 상속받는게 정석


        @Override
        public void handleMessage(@NonNull Message msg) {
            // msg에서 arg1에 저장된 값을 setText

            ((TextView)msg.obj).setText(msg.arg1 + "");  // setText 할 때 정수형을 넣으면 에러!! (정수형을 참조할 주소값으로 인식함)
            // obj로만든 TextView로 다운캐스팅하고 setText함
        }
   };


    class cntThread extends Thread {
        private TextView textView;

        public cntThread(TextView textView){    // 매개변수로 TextView를 전달받는 생성자
            this.textView=textView;
        }

        @Override
        public void run() { //Thread 시작시켰을 때
            // Thread가 할 일을 적어주면 됨 - 1~10까지 1초씩 세는 코드

            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    return;
                    // 메소드는 return문을 만나면 종료되기 때문에
                    // 누군가가 이 Thread를 종료시키고 싶다면 interrupt를 걸어주면 됨

                    // Thread가 CPU 사용하다가 양보 -> yield
                    // CPU 뺏어옴 -> interrupt
                    // 쉬는동안 (sleep) 다른 Thread가 interrupt를 걸었을 때를 대비
                }

                // i -> TextView에 setText만 하면 됨

                // 여기 (SubThread -> 내가 만든 Thread)에서는 setText등 UI업데이트 못함
                // Handler에 메세지를 전송해서 setText작업을 처리해야함

                Message msg = new Message();
                // Message 객체에는 변수가 3개있음 ▼
                // Object obj , int arg1 , int arg2  -  변수에 각각 값을 저장할 수 있음

                // i값을 handler에 보내서 textView에 setText 하기
                msg.arg1 = i;
                msg.obj = textView;
                handler.sendMessage(msg);
            }
        }
    }
}

버튼2를 똑같이 만들어 눌렀을때 tv2가 변하게 만든다

 

 

 

 


 

 

 

연결할 이미지 동시선택 -> 우클릭해서 chain 생성하기

 

●fragment_3.java

package com.kmg.ex0413_dudu;

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

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

import java.util.Random;

public class MainActivity extends AppCompatActivity {

    TextView tv_count;
    ImageView[] imgArray = new ImageView[9];

    int score;

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

        tv_count = findViewById(R.id.tv_count);

        for(int i = 0; i<imgArray.length; i++){

            // 상수 정의
            final int pos = i;

            // 이미지뷰초기화
            int img_id = getResources().getIdentifier("img"+(i+1),"id",getPackageName());
                                            //매개변수 name -> View의 id
                                            //매개변수 defType -> id ( R.id )
                                            //매개변수 defPackage -> Project의 pakage
            imgArray[i] = findViewById(img_id);

            // 두더지의 상태(on/off)를 이미지에 저장
            imgArray[i].setTag("off");

            // 두더지 쓰레드 실행
            Thread doThread = new doThread(i);
            doThread.start();

            // 클릭이벤트 적용
            imgArray[i].setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    // 두더지 상태에 따른 점수 카운트 (Tag사용)
                    if (imgArray[pos].getTag().toString().equals("on")){  // 변수x , 상수o ( 변수 i대신 pos라는 상수로 )
                        score++;
                    }else{
                        score--;
                    }
                    tv_count.setText(String.valueOf(score));    // score 문자열로 반환
                }
            });



        }
    }//end onCreate()

    Handler dohandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {

            // 두더지에 대한 상태, 이미지 등을 변경
            imgArray[msg.arg1].setImageResource(msg.arg2);
            imgArray[msg.arg1].setTag(msg.obj);

        }
    };



    class doThread extends Thread{

        int pos = 0;    // 두더지 번호

        public doThread(int pos) {  // 두더지 번호 초기화
            this.pos = pos;
        }

        @Override
        public void run() {

            while (true) {
                // 박혀있는 시간
                int offtime = new Random().nextInt(5000) + 500;  // 0 ~ 4.5초
                // 올라온 시간
                int ontime = new Random().nextInt(3000) + 500;  // 0 ~ 4.5초


                try {
                    Thread.sleep(offtime);

                    Message msg = new Message();

                    // 핸들러에게 전달할 값 - pos 몇번째 두더지인지, on.png파일명, 상태값(on)
                    msg.arg1 = pos;
                    msg.arg2 = R.drawable.on;
                    msg.obj = "on";

                    dohandler.sendMessage(msg);


                    // 두더지 숨기는 기능 구현
                    // 두더지가 서있는 시간
                    // 두더지 번호, 사진, 상태값을 새로운 Message객체를 생성해 핸들러에게 전달


                    Thread.sleep(ontime);

                    msg = new Message();
                    msg.arg1 = pos;
                    msg.arg2 = R.drawable.off;
                    msg.obj = "off";

                    dohandler.sendMessage(msg);


                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }   // while문
        }
    }
}
반응형