본문 바로가기

대학교 과제/모바일프로그래밍 [ 2 - 2 ]

[모바일 프로그래밍 과제] - 8주차 (직접풀어보기 8 - 2)

반응형

1. 과제 안내문, 예시 출력화면


과제 #8

직접 풀어보기 8-2

교재 내용을 확인하고 해결해서 제출합니다.

단, 상단의 그림 번호는 그림이 3개라면 1/3 ~ 3/3으로 표시되도록 합니다(0이 아니라 1에서 시작해야 하니 주의~)

제출방법: xml 및 자바 파일, 실행 화면 캡쳐 3장 이상을 압축하여 zip 형식으로 제출합니다. 커스텀뷰 파일을 꼭 포함합니다.

 

예시실행화면

 


 중간고사 이후 블로그의 업데이트가 많이 늦어지게 되었다. 밀린 글들이 정말 많다... 과제 세개를 동시에 올리니 체력의 한계가 느껴지지만, 방금 과제 하나가 더 올라왔다... 웹프로그래밍에 대한 글도 최대한 빨리 올릴 예정이다.

 

2. 문제풀이

 

이번 과제는  SD카드에 있는 데이터를 다루는 과제이다. 때문에 커스텀과 같은 부분은 다루지 않겠다. (저도 그냥 책에 있는 그대로 썼어요...) 다음은 커스텀한 자바파일과 XML코드이다.

 

- myPictureView.java 코드

 

public class myPictureView extends View {
    String imagePath = null;
    public myPictureView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    protected void onDraw(Canvas canvas){
        super.onDraw(canvas);
        if(imagePath != null){
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            canvas.drawBitmap(bitmap,0,0,null);
            bitmap.recycle();
        }
    }
}

 

- activity_main.xml 코드

 

<?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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/btnPrev"
            android:text="이전그림"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="1"
            android:textSize="30dp"
            android:id="@+id/tvNum"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:textSize="30dp"
            android:text="/"/>

        <TextView
            android:id="@+id/tvSize"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:textSize="30dp"
            android:text=""/>

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/btnNext"
            android:text="다음그림"/>
    </LinearLayout>
    <com.cookandroid.training8_2.myPictureView
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:id="@+id/myPictureView1"/>
</LinearLayout>

 

XML코드의 마지막 위젯을 보면, 내가 작성한 java Class를 볼 수 있다. 지금은 '이런 방식으로 사용하면 이미지를 출력할 수 있구나!' 라고 생각하면서 넘기기만 하자. 

 

 이제 이 과제에서 핵심이라고 할 수 있는 SD카드 내부의 메모리를 처리하는 코드이다. 일단 앱 내에서 SD카드를 사용하려면 관련된 퍼머션(권한)을 설정해야한다. 이는 AndroidManifest.xml 파일에 다음 두 줄을 추가함으로써 설정할 수 있다.

 

-SD카드 사용을 위한 퍼머션 및 속성 추가 코드

 

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
        android:requestLegacyExternalStorage="true"
    </application>

 

 여기서 중요한점은 android:requestLegacyExternalStorage="true"의 속성을 application 태그 안의 속성으로 추가해야한다는 것이다. 이제 SD 카드에 파일을 추가하면 된다. SD 카드에 파일을 추가할 때는 디바이스의 파일들을 쉽게 접근할 수 있는 Device File Explorer를 사용하면 편하다. Device File Explorer는 상단 메뉴바에서 View -> Tool Windows를 통해 열 수 있다. 다음은 관련된 이미지이다.



 이 창을 열면 여러 폴더가 나오게 되는데, 여기서 sdcard라는 폴더에 데이터를 저장하면 된다. 나는 과제에서 제공된 이미지 5개를 다음과 같이 저장하였다.



 이 과제에서는 Bitmap을 통해 이미지를 출력하는데 이는 자세하게 다루지 않겠다. (책에서도 해당 단원에서는 자세하게 다루지 않았습니다...) 지금 중요한 것은 "SD카드에 저장되어있는 파일들을 가지고 오는 법" 이다. 따라서 다음과 같은 메소드를 통해 파일을 리스트형태로 가지고 올 수 있다.

 

imageFiles = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pictures").listFiles();

 

 여기서 imageFiles는 File클래스를 요소로 가지는 리스트이다. 위의 코드를 조금이나마 해석하자면, Environment.getExternalStorageDirectory().getAbsolutePath() 메소드는 SD카드의 경로를 반환한다. 즉 여기서 "/Pictures"를 붙여주면서, 해당파일에 있는 데이터들을 전부 리스트 형태로 반환받을 수 있는 것이다. 다음은 전체 자바 코드이다.

 

- MainActivity.java

 

package com.cookandroid.training8_2;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;

public class MainActivity extends AppCompatActivity {
    Button btnPrev, btnNext;
    myPictureView myPicture;
    TextView  tvNum, tvSize;
    int curNum;
    File[] imageFiles;
    String imageFname;

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

        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, MODE_PRIVATE);
        btnPrev = (Button) findViewById(R.id.btnPrev);
        btnNext = (Button) findViewById(R.id.btnNext);
        myPicture = (myPictureView) findViewById(R.id.myPictureView1);

        tvNum = (TextView) findViewById(R.id.tvNum);
        tvSize = (TextView) findViewById(R.id.tvSize);



        imageFiles = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pictures").listFiles();
        imageFname = imageFiles[0].toString();
        myPicture.imagePath = imageFname;

        tvSize.setText(Integer.toString(imageFiles.length));

        btnPrev.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if(curNum <=0){
                    curNum = imageFiles.length - 1;
                }else{
                    curNum--;
                }
                int buff = curNum + 1;
                tvNum.setText(Integer.toString(buff));
                imageFname = imageFiles[curNum].toString();
                myPicture.imagePath = imageFname;
                myPicture.invalidate();
            }
        });

        btnNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(curNum >= imageFiles.length - 1){
                    curNum = 0;
                }else{
                    curNum++;
                }
                int buff = curNum + 1;
                tvNum.setText(Integer.toString(buff));
                imageFname = imageFiles[curNum].toString();
                myPicture.imagePath = imageFname;
                myPicture.invalidate();
            }
        });
    }
}

 

 위의 코드를 조금이나마 설명하자면, myPicture.imagePath에 해당파일의 이름 즉 리스트에 저장된 요소중 하나를 대입하고 myPicture.invalidate(); 메소드를 실행하면 저장된 파일로 그림이 바뀐다고 생각하고 넘기면 조금 편할것이다.

반응형