오늘이라도

[Android] 21. 리스트 뷰(List View) ③ : 이미지 클릭 시 팝업창 띄우기, 팝업창에 클릭한 이미지 넣기 / 그리드 뷰(Grid View) : 프로젝트 복사, 이름 바꾸기, 라이브러리 추가 본문

취업성공패키지 SW 개발자 교육/Android

[Android] 21. 리스트 뷰(List View) ③ : 이미지 클릭 시 팝업창 띄우기, 팝업창에 클릭한 이미지 넣기 / 그리드 뷰(Grid View) : 프로젝트 복사, 이름 바꾸기, 라이브러리 추가

upcake_ 2020. 6. 3. 10:58
반응형

https://github.com/upcake/Class_Examples

교육 중에 작성한 예제들은 깃허브에 올려두고 있습니다. 

gif 파일은 클릭해서 보는 것이 정확합니다.


 - 리스트 뷰 ③ : 이미지 클릭 시 팝업창 띄우기, 팝업창에 클릭한 이미지 넣기 -

▲이미지 팝업 방법 ① : 팝업창에 이미지뷰 넣기

 

▲이미지 팝업 방법 ② : 팝업과 XML 연결

 

▼activity_main.xml

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

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="추가"
        android:textSize="24sp" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:listSelector="@android:color/holo_blue_bright"/>

</LinearLayout>

 

▼MainActivity.java

package com.example.my27_listview2;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;

/*
1. DB를 만든다
2. DB의 테이블과 똑같은 DTO를 만든다
3. DTO에서 표현하고자 하는 부분을 xml로 만든다
4. 어댑터를 만든다
5. 어댑터에 3에서 만든 xml을 표시할 ViewHolder Class를 만든다
    5 - 1 : ViewHolder 작성
    5 - 2 : xml 찾기
    5 - 3 : getItem > Data 넣기
    5 - 4 : 필요한 메서드 만든다.

메인 액티비티 - 리스트 뷰(껍데기)
껍데기에 내용물을 만들어서 보여주는 것이 어댑터
 */

public class MainActivity extends AppCompatActivity {
    //객체 선언
    Button button1;
    ListView listView;

    ArrayList<SingerDTO> list;

    SingerAdapter adapter;

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

        //디바이스 사이즈 구하기
        Point size = getDeviceSize();

        //객체 초기화
        button1 = findViewById(R.id.button1);
        listView = findViewById(R.id.listView);

        list = new ArrayList<>();

        //어댑터의 list에 데이터 추가
        adapter = new SingerAdapter(MainActivity.this, list, size);
        adapter.addDTO(new SingerDTO("가수1", "010-1111-1111", 30, R.drawable.singer1));
        adapter.addDTO(new SingerDTO("가수2", "010-2222-2222", 21, R.drawable.singer2));
        adapter.addDTO(new SingerDTO("가수3", "010-3333-3333", 25, R.drawable.singer3));
        adapter.addDTO(new SingerDTO("가수4", "010-4444-4444", 33, R.drawable.singer4));
        adapter.addDTO(new SingerDTO("가수5", "010-5555-5555", 38, R.drawable.singer5));

        //리스트뷰에 어댑터를 붙여준다
        listView.setAdapter(adapter);

        //리스트뷰의 아이템 클릭했을때 이벤트 추가
        //AdapterView<?> parent : 클릭이 발생한 어댑터뷰
        //View view : 어댑터뷰 내부의, 클릭이 된 바로 그 뷰
        //int position : 어댑터 내부의 그 뷰의 위치(position)
        //long id : 클릭된 아이템의 row id
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                SingerDTO dto = (SingerDTO) adapter.getItem(position);
                Toast.makeText(MainActivity.this, "선택 : " + position + "\n이름 : " + dto.getName()
                        + "\n전화번호 : " + dto.getPhoneNum() +"\n나이 : " + dto.getAge() + "\n이미지 : " +dto.getResId(), Toast.LENGTH_SHORT).show();
            }
        });

        //버튼1(추가 버튼)에 기능 추가
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = "가수6";
                String phoneNum = "010-6666-6666";
                int age = 35;
                int resId = R.drawable.image01;

                SingerDTO dto = new SingerDTO(name, phoneNum, age, resId);
                adapter.addDTO(dto);
                //adapter.addDTO = (new SingerDTO(name, phoneNum, age, resId));
                //adapter.addDTO = (new SingerDTO("가수6", "010-6666-6666", 35, R.mipmap.ic_launcher);

                //리스트뷰 데이터 갱신
                adapter.notifyDataSetChanged();
            }
        });
    }

    // 디바이스 가로 세로 사이즈 구하기
    // getRealSize()는 status bar 등 system insets을
    // 포함한 스크린 사이즈를 가져오는 방법이고,
    // getSize()는 status bar 등 insets를
    // 제외한 부분에 대한 사이즈만 가져오는 함수이다.
    // 단위는 픽셀
    public Point getDeviceSize() {
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getRealSize(size);

        //현재 프로젝트에서는 쓰지 않지만 가로와 세로 길이를 이렇게 빼서 사용한다
        int width = size.x;
        int height = size.y;

        return size;
    }
}

 

▼SingerAdapter.java

package com.example.my27_listview2;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Point;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class SingerAdapter extends BaseAdapter {
    //어댑터에 데이터를 받기위해 생성자 만든다.
    // 컨텍스트와 리스트는 받아오지만 인플레이터는 안받아온다.
    Context context;
    ArrayList<SingerDTO> list;
    Point size;

    LayoutInflater inflater;
    AlertDialog dialog;

    public SingerAdapter(Context context, ArrayList<SingerDTO> list, Point size) {
        this.context = context;
        this.list = list;
        this.size = size;

        this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        //인플레이터는 시스템에서 가져온다.
    }

    //리스트(list)에 항목을 추가해 줄 메서드 작성
    public void addDTO(SingerDTO dto) {
      list.add(dto);
    }

    //리스트의 항목을 삭제할 메서드 작성
    public void delDTO(int position) {
        list.remove(position);
    }

    //리스트의 항목을 모두 삭제할 메서드 작성
    public void removeDTOs() {
        list.clear();
    }

    //getCount() : 리스트에서 항목을 몇개나 가져와서 몇개의 화면을 만들 것인지 정하는 메서드
    @Override
    public int getCount() {
        return list.size();
    }

    //getItem() : 리스트에서 해당하는 인덱스의 데이터(사진, 이름, 전번)를 모두 가져오는 메서드
    //Object를 알아서 캐스팅해서 사용하라는 의미로 반환 타입이 Object
    @Override
    public Object getItem(int position) {
        return list.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        SingerViewHolder viewHolder;

        //화면 구성
        if(convertView == null) {
            convertView = inflater.inflate(R.layout.singerview, parent, false);
            viewHolder = new SingerViewHolder();
            viewHolder.tvName = convertView.findViewById(R.id.tvName);
            viewHolder.tvPhoneNum = convertView.findViewById(R.id.tvPhoneNum);
            viewHolder.imageView = convertView.findViewById(R.id.imageView);

            convertView.setTag(viewHolder);
        } else {
            viewHolder = (SingerViewHolder) convertView.getTag();
        }

        //DTO에서 데이터를 찾음
        SingerDTO dto = list.get(position);
        String name = dto.getName();
        String phoneNum = dto.getPhoneNum();
        int resId = dto.getResId();

        //XML의 화면에 찾은 데이터 표시
        viewHolder.tvName.setText(name);
        //viewHolder.tvName.setText(dto.getName()); // 이렇게 써도 같음
        viewHolder.tvPhoneNum.setText(phoneNum);
        viewHolder.imageView.setImageResource(resId);

        //이미지만 클릭했을때 기능 추가
        viewHolder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, "선택 : " + position
                        + ", 이름 : " + list.get(position).getName(), Toast.LENGTH_SHORT).show();
                //방법 ① : 이미지뷰 추가하여 직접 붙임
                //popUpImg(list.get(position).getResId());

                //방법 ② : 미리 만들어둔 XML과 팝업창을 연결해서 보여줌
                popupImgXml(list.get(position).getResId(), list.get(position).getName());
            }
        });

        return convertView;
    }

    //따로 새 자바 파일을 만들지 않고 XML의 내용을 볼 수 있게끔 만든 클래스
    public class SingerViewHolder {
        public ImageView imageView;
        public TextView tvName, tvPhoneNum;
    }

    //방법 ① : 이미지 뷰를 만들어서 직접 이미지 넣기
    public void popUpImg(int resId) {
        ImageView image = new ImageView(context);
        image.setImageResource(resId);

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("이미지 띄우기");
        builder.setView(image);

        builder.setNegativeButton("종료", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if(dialog != null) {
                    dialog.dismiss();
                }
            }
        });
        dialog = builder.create();
        dialog.show();
    }

    //방법 ② : XML 불러오기
    public void popupImgXml(int resId, String name) {
        //일단 res에 popupimg.xml 만든다
        //그 다음 화면을 inflate 시켜 setView 한다

        //팝업창에 xml 붙이기///////////////
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.popupimg, null);
        ImageView imageView = view.findViewById(R.id.imageView);
        TextView textView = view.findViewById(R.id.textView);

        imageView.setImageResource(resId);
        textView.setTextSize(35);
        textView.setText(name + "\n");
        textView.append(name + "\n" + name + "\n" + name + "\n" + name + "\n" + name + "\n" + name + "\n" + name + "\n" + name + "\n");
        /////////////////////////

        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("이미지 띄우기")
                .setView(view);

        builder.setNegativeButton("종료", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if(dialog != null) {
                    dialog.dismiss();
                }
            }
        });
    dialog = builder.create();
    dialog.show();

    //디바이스 사이즈를 받아 팝업 크기창을 조절한다.
    int sizeX = size.x;
    int sizeY = size.y;

    //AlertDialog에서 위치 크기 수정
    WindowManager.LayoutParams params = dialog.getWindow().getAttributes();

    params.x = (int) Math.round(sizeX * 0.005); // X위치
    params.y = (int) Math.round(sizeY * 0.01); // Y위치
    params.width = (int) Math.round(sizeX * 0.9);
    params.height = (int) Math.round(sizeY * 0.8);
    dialog.getWindow().setAttributes(params);
    }
}

 

▼SingerDTO.java

package com.example.my27_listview2;

public class SingerDTO {
    String name;
    String phoneNum;
    int age;
    int resId;

    public SingerDTO() {}

    public SingerDTO(String name, String phoneNum, int age, int resId) {
        this.name = name;
        this.phoneNum = phoneNum;
        this.age = age;
        this.resId = resId;
    }

    public String getName() {
        return name;
    }

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

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    public int getAge() {
        return age;
    }

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

    public int getResId() {
        return resId;
    }

    public void setResId(int resId) {
        this.resId = resId;
    }
}

 

▼singerview.xml

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:src="@drawable/singer1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:gravity="center_vertical"
            android:text="Name"
            android:textColor="@android:color/holo_green_light"
            android:textSize="35sp" />

        <TextView
            android:id="@+id/tvPhoneNum"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:gravity="center_vertical"
            android:text="PhoneNum"
            android:textColor="@android:color/holo_orange_light"
            android:textSize="25sp" />

    </LinearLayout>


</LinearLayout>

 

▼popupimg.xml

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"/>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>

 

 

- 그리드 뷰(Grid View) : 프로젝트 복사, 이름 바꾸기 / 라이브러리 추가 -

□ 프로젝트 복사, 이름 바꾸기

▲패키지 이름 변경

 - My27_ListView2 프로젝트 폴더의 사본을 만들고 이름을 My28_GridView로 바꾼다.

 - app - java - com.example.my27_listview2 우클릭 - Refactor - Rename - Rename Package

 

▲build.gradle 변경

 - build.gradle의 applicationId를 패키지 이름에 맞게 변경한다.

 

▲실행 화면 상단 표시 이름 변경

 - res - values - strings.xml 에서 어플리케이션 실행 화면 상단의 표시 이름을 변경한다.

 

□ 라이브러리 추가

▲라이브러리 추가

 - 상단 메뉴바 File - Project Structure - Dependencies - +버튼 - Library Dependencies - design 검색 - com.android.support 추가

 

▲추가 후 build.gradle

 - 버전을 28.0.0에서 29.0.0으로 바꾼다.

 

□ 작동 화면 및 코드

▲그리드 뷰 작동 화면

 

※ 위의 ListView2 프로젝트에서 변경된 부분만 코드를 작성했습니다.

▼activity_main.xml

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

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="추가"
        android:textSize="24sp" />

    <GridView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:listSelector="@android:color/holo_blue_bright"
        android:numColumns="2"
        android:horizontalSpacing="8dp"
        android:verticalSpacing="8dp"
        android:layout_margin="8dp"/>

</LinearLayout>

 

▼MainActivity.java

package com.example.my28_gridview;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Point;
import android.os.Bundle;
import android.view.Display;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ListView;
import android.widget.Toast;

import java.util.ArrayList;

/*
1. DB를 만든다
2. DB의 테이블과 똑같은 DTO를 만든다
3. DTO에서 표현하고자 하는 부분을 xml로 만든다
4. 어댑터를 만든다
5. 어댑터에 3에서 만든 xml을 표시할 ViewHolder Class를 만든다
    5 - 1 : ViewHolder 작성
    5 - 2 : xml 찾기
    5 - 3 : getItem > Data 넣기
    5 - 4 : 필요한 메서드 만든다.

메인 액티비티 - 리스트 뷰(껍데기)
껍데기에 내용물을 만들어서 보여주는 것이 어댑터
 */

public class MainActivity extends AppCompatActivity {
    //객체 선언
    Button button1;
    GridView gridView;

    ArrayList<SingerDTO> list;

    SingerAdapter adapter;

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

        //디바이스 사이즈 구하기
        Point size = getDeviceSize();

        //객체 초기화
        button1 = findViewById(R.id.button1);
        gridView = findViewById(R.id.listView);

        list = new ArrayList<>();

        //어댑터의 list에 데이터 추가
        adapter = new SingerAdapter(MainActivity.this, list, size);
        adapter.addDTO(new SingerDTO("가수1", "010-1111-1111", 30, R.drawable.singer1));
        adapter.addDTO(new SingerDTO("가수2", "010-2222-2222", 21, R.drawable.singer2));
        adapter.addDTO(new SingerDTO("가수3", "010-3333-3333", 25, R.drawable.singer3));
        adapter.addDTO(new SingerDTO("가수4", "010-4444-4444", 33, R.drawable.singer4));
        adapter.addDTO(new SingerDTO("가수5", "010-5555-5555", 38, R.drawable.singer5));

        //리스트뷰에 어댑터를 붙여준다
        gridView.setAdapter(adapter);

        //리스트뷰의 아이템 클릭했을때 이벤트 추가
        //AdapterView<?> parent : 클릭이 발생한 어댑터뷰
        //View view : 어댑터뷰 내부의, 클릭이 된 바로 그 뷰
        //int position : 어댑터 내부의 그 뷰의 위치(position)
        //long id : 클릭된 아이템의 row id
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                SingerDTO dto = (SingerDTO) adapter.getItem(position);
                Toast.makeText(MainActivity.this, "선택 : " + position + "\n이름 : " + dto.getName()
                        + "\n전화번호 : " + dto.getPhoneNum() +"\n나이 : " + dto.getAge() + "\n이미지 : " +dto.getResId(), Toast.LENGTH_SHORT).show();
            }
        });

        //버튼1(추가 버튼)에 기능 추가
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = "가수6";
                String phoneNum = "010-6666-6666";
                int age = 35;
                int resId = R.drawable.image01;

                SingerDTO dto = new SingerDTO(name, phoneNum, age, resId);
                adapter.addDTO(dto);
                //adapter.addDTO = (new SingerDTO(name, phoneNum, age, resId));
                //adapter.addDTO = (new SingerDTO("가수6", "010-6666-6666", 35, R.mipmap.ic_launcher);

                //리스트뷰 데이터 갱신
                adapter.notifyDataSetChanged();
            }
        });
    }

    // 디바이스 가로 세로 사이즈 구하기
    // getRealSize()는 status bar 등 system insets을
    // 포함한 스크린 사이즈를 가져오는 방법이고,
    // getSize()는 status bar 등 insets를
    // 제외한 부분에 대한 사이즈만 가져오는 함수이다.
    // 단위는 픽셀
    public Point getDeviceSize() {
        Display display = getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getRealSize(size);

        //현재 프로젝트에서는 쓰지 않지만 가로와 세로 길이를 이렇게 빼서 사용한다
        int width = size.x;
        int height = size.y;

        return size;
    }
}

 

▼singerview.xml

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

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@drawable/singer1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="vertical"
        android:layout_marginLeft="5dp">

        <TextView
            android:id="@+id/tvName"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:gravity="center_vertical"
            android:text="Name"
            android:textColor="@android:color/holo_green_light"
            android:textSize="16sp" />

        <TextView
            android:id="@+id/tvPhoneNum"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:gravity="center_vertical"
            android:text="PhoneNum"
            android:textColor="@android:color/holo_orange_light"
            android:textSize="16sp" />

    </LinearLayout>


</LinearLayout>
반응형