반응형

(210518 수정)

 

카메라는 우리가 스마트폰 앱을 사용하면서 가장 많이 사용하는 앱 중 하나이다.

 

그래서 이번에는

카메라 미리보기(Camera Preview)를 통해 사진을 촬영하고

촬영한 사진이 이미지뷰에 나타나는 기능을 구현해볼 것이다.

 

카메라 미리보기 기능은

스마트폰 단말에 있는 기본 카메라 앱을 사용하기 때문에

기본 카메라 앱이 제공하는 기능을 그대로 사용할 수 있어서 편리하다.

 

그리고 카메라를 촬영하기 전 미리보기 화면 구현

'서피스뷰(SurfaceView)' 라는 것을 이용해야하기 때문에

우선 서피스뷰부터 알아보겠다.

 

서피스뷰(SufaceView)란?

간단하게 말해서 그리기 전용의 화면을 제공하는 뷰(View)이다.

 

서피스뷰를 만든 이유는 UI와는 독립적으로 애플리케이션에게 그림을

그릴 수 있는 화면(별도의 스레드, 별도의 스케치북이라고 생각)은 제공하는 것이다.

 

서피스뷰를 사용하면 UI가 만들어질 때까지 기다리지 않아도 된다.

무슨말이냐면, 화면에 직접 그리는 것이 아니라

먼저 서피스뷰가 그리고 이것을 안드로이드 시스템이 화면으로 복사하는 것이다.

 

그런데 서피스뷰는 사실 껍데기일 뿐이고 실제로는 서피스 홀더 객체가 컨트롤을 한다.

이것에 집중하면서 서피스뷰를 이용한 카메라 프리뷰 기능을 공부해보겠다.

 

activity_main.xml

 

----------생략------------

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<FrameLayout
        android:layout_width="412dp"
        android:layout_height="580dp"
        android:layout_marginTop="4dp"
        app:layout_constraintTop_toBottomOf="@+id/imageView"
        tools:layout_editor_absoluteX="0dp"
        tools:ignore="MissingConstraints">
 
        <com.example.cameracapturetest.CameraSurfaceView
            android:id="@+id/surfaceView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
 
    </FrameLayout>
cs

FrameLaylout을 추가하고 그 안에 서피스뷰를 넣을 것이다.

 

그러면 서피스뷰가 껍데기인채로 추가가 될 것이다.

 

이제 독립적인 서피스뷰 클래스를 만들고

서피스 홀더를 이용해서 서피스뷰를 컨트롤 해보겠다.

 

CameraSurfaceView.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class CameraPreView extends SurfaceView implements SurfaceHolder.Callback {
 
    // 서피스뷰는 껍데기만 있는 것이기 때문에 실제 컨트롤은 서피스 홀더가 해줌
 
    SurfaceHolder holder; // 서피스 홀더 객체
    Camera camera = null// 카메라 객체에 밑줄이 그어져 있는 이유는 카메라2가 나오면서 지원이 중단됨
 
    public CameraPreView (Context context) {
        super(context);
 
        init(context);
    }
 
    public CameraPreView (Context context, AttributeSet attrs) {
        super(context, attrs);
 
        init(context);
    }
 
    private void init(Context context) { // 초기화를 위한 메서드
        holder = getHolder(); // 서피스뷰 내에 있는 서피스홀더 객체를 참조할 수 있음
        holder.addCallback(this); // 콜백 인터페이스 등록
    }
 
    @Override
    public void surfaceCreated(SurfaceHolder holder) { // 서피스뷰가 메모리에 만들어지는 시점에 호출됨됨
            camera = Camera.open(); // 카메라 오픈 -> 카메라 객체 참조
 
            try {
                camera.setPreviewDisplay(holder); // 참조한 카메라 객체에 서피스홀더 객체를 지정 ->  카메라 객체에 이 서피스뷰를 미리보기로 쓸 것임
            } catch (IOException e) {
                e.printStackTrace();
            }
    }
 
    @Override // 서피스뷰가 변경되는 시점에 호출됨, 화면에 보여지기 전에 크기가 결정되는 시점
    public void surfaceChanged(SurfaceHolder holder, int formatint width, int height) {
        camera.startPreview(); // 미리보기 화면을 뿌리기 시작
    }
 
    @Override // 서피스뷰가 없어질 때 호출됨
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview(); // 미리보기 화면은 많은 리소스를 잡아 먹기 때문에 멈추고
        camera.release(); // release를 통해 리소스를 없앰
        camera = null// 카메라 해제
    }
 
    public boolean capture(Camera.PictureCallback callback) { // 서피스뷰에서 사진을 찍는 메서드
        if(camera != null) {
            camera.takePicture(nullnull , null, callback); // 사진 촬영
            return true;
        } else {
            return false;
        }
    }
}
cs

 

그리고 이 앱은 카메라를 접근하므로 

매니페스트에 권한을 추가해줘야 한다.

 

 

AndroidManifest.xml

 

-------------생략---------------

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

 

-------------생략---------------

 

이제 메인 액티비티에서 카메라 미리보기 화면으로

사진을 찍을 수 있게 해주면 끝!

 

MainActivity.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class MainActivity extends AppCompatActivity {
 
   ImageView iv;
   CameraPreView preView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
       iv = (ImageView)findViewById(R.id.iv);
       preView = (CameraSurfaceView)findViewById(R.id.surfaceView);
 
       Button btn = (Button)findViewById(R.id.btn);
       btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               takePicture();
            }
        });
    }
 
    public void takePicture() { 
       preView.capture(new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(byte[] data, Camera camera) { // 사진이 찍어지면 여기에 byte[]가 전달됨
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 8// 파일로 저장한 다음에 로딩할 때와 동일한 상황이 됨, 비트맵 객체는 1/8크기로 생성됨
                Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); // 데이터를 가지고 비트맵 객체 생성
 
               iv.setImageBitmap(bitmap); // 비트맵을 이미지뷰에 설정해서 보여주기
 
                // 사진을 찍으면 미리보기가 중지가 되기 때문에
                camera.startPreview(); // 미리보기를 다시 시작함
            }
        });
    }
}
cs

(기본 모드가 가로화면이라서 뒤집어져서 보이게 된다.

세로 모드는 추후에 업로드 예정.)

 

피드백은 언제나 환영입니다.

반응형
반응형

랜덤 확률은 우리의 일상생활이나 게임에서 많이 찾아볼 수 있듯이,

프로그래밍에서 굉장히 많이 쓰이는 것 중 하나이다.

 

이러한 랜덤 확률을 만드는 메소드인 Math.random()에 대해서 알아보자.

 

Math.random()이란?

0 이상 1 미만의 구간에서 부동 소수점 난수를 생성하는 함수이다.

(0.0 <= x < 1.0, 즉, 실수(double) 값을 리턴)

 

그럼 이제 확률을 구현해보자.

예를들어 70%의 확률로 성공확률이 있다고 해보자.

1
2
3
4
5
6
7
if (Math.random() <= 0.7) {
             result.setText("성공");
            } else {
           result.setText("실패");
            }
cs

이런식으로 하면된다.

 

Math.random() 메소드는 0부터 1사이 소수를 섞으니까

그 난수가 0.7보다 작거나 같으면 70%의 확률로 성공이 되는 것이다.

하지만 난수가 0.7보다 크다면 30%의 확률로 실패가 되는 것이다.

 

간단하지만 확률이 필요할 때 요긴하게 사용하길 바란다.

 

피드백은 언제나 환영입니다.

반응형
반응형

각 종 개념들 : www.tcpschool.com/

반응형
반응형

(210518 수정)

 

앱을 사용하다보면 옵션을 선택하는 경우를 종종 볼 수가 있다.

안드로이드에는 많은 메뉴 기능이 있지만 이번에 우리가 알아볼 기능은

'스피너(Spinner)' 이다.

 

스피너는 우리가 값들을 선택할 수 있는 빠른 방법중에 하나이고

스피너를 누르면 값들이 드롭다운이 되며

값을 선택하면 현재 선택된 값을 표시하게된다.(기본 상태)

 

먼저 스피너 위젯을 레이아웃에 추가를 하고

 

activity_main.xml 에 스피너 위젯 추가

1
2
3
4
<Spinner
        android:id="@+id/spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
cs

스피너 안에 넣을 값 집합 value/spinner_item.xml 로 만든다.

 

spinner_item.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">스피너 예시</string>
 
<string-array name="spinner_array" >
        <item>선택 안됨</item>
        <item>하빌리즘</item>
        <item>브레이브걸스</item>
        <item>메이플스토리</item>
        <item>서든어택</item>

    </string-array>
</resources>
cs

 

 

이제 스피너와 값 집합을 만들었으니

동작을 구현해보자.

 

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
spinner = (Spinner)findViewById(R.id.spinner);
 
        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( // 스피너와 값 집합의 연결을 위한 어레이 어댑터 생성
                this, R.array.spinner_array, android.R.layout.simple_spinner_item); 
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); // 미리 정의된 드롭다운 레이아웃 사용
        spinner.setAdapter(adapter); // 스피너와 어댑터를 연결
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { // 스피너의 값들을 선택하면 호출
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if(position == 0) {
                    Toast.makeText(getApplicationContext(), "선택안됨 ", Toast.LENGTH_SHORT).show();
                   
                } else if(position == 1) {
                    Toast.makeText(getApplicationContext(), "하빌리즘 선택 ", Toast.LENGTH_SHORT).show();
                  
                } else if(position == 2) {
                    Toast.makeText(getApplicationContext(), "브레이브걸스 선택", Toast.LENGTH_SHORT).show();
                 
                } else if(position == 3) {
                    Toast.makeText(getApplicationContext(), "메이플스토리 선택 ", Toast.LENGTH_SHORT).show();
               
                } else if(position == 4) {
                    Toast.makeText(getApplicationContext(), "서든어택 선택 ", Toast.LENGTH_SHORT).show();
                
                }
 
            }
 
            @Override
            public void onNothingSelected(AdapterView<?> parent) {
 
            }
        });
 
cs

 

선택 안됨
드롭다운으로 보여주는 값 집합
현재 선택되면 표시해줌

 

피드백은 언제나 환영입니다.

반응형
반응형

(210518 수정)

url을 입력하기 전

 

url을 입력한 후

웹 서버에 요청하고 응답받는 방식은 여러가지가 있지만

그 중에서 가장 많이 사용되고 효율적인 것 중 하나가 바로 지금 알아볼

'Volley' 라는 것이다.

 

Volley 라이브러리는 웹 요청과 응답을 간단하게 하기 위해 만들어진 라이브러리로써

요청(Request) 객체를 만들고 이것을 그냥 요청 큐(RequestQueue)에 넣어주기만 하면

쉽게 처리할 수가 있다.

(요청 큐가 알아서 웹 서버에 요청하고 응답까지 받아주는.. 그냥 다 해준다는 것이다.

심지어 메인 스레드에서 UI에 접근할 때 핸들러를 사용할 필요도 없이

이것마저 다 해준다...!) 

 

결국 우리는 요청 큐가 우리가 보낸 응답을 받을 수 있도록 메서드만 만들어두기만 하면

그 메서드가 자동으로 호출된다.

 

그리고 Volley는 여러 장점이 있지만 가장 큰 장점은

위에 언급했던 스레드를 신경쓰지 않아도 되고, 코드의 양이 적다는 것이다.

 

일단 기능이 어떻게 구현되는지

아주 간단하게 알아보겠다.

 

우선 Volley 외부 라이브러리부터 추가해보자.

Gradle Scripts안에 있는 build.gradle(Module) 파일로 열어서

dependencies에 implementation 'com.android.volley:volley:1.1.0' 추가하고 Sync Now를 누른다.

그럼 Volley 라이브러리가 추가가 된다.

 

그리고 웹 서버와 요청과 응답을 주고 받아야하기 때문에

매니페스트 파일에 인터넷 권한을 준다.

<uses-permission android:name="android.permission.INTERNET"/>

 

화면을 나타낼 activity_mail.xml에는 특별히 별거 없지만

그나마 신경 써야하는 것이 있는데,

웹 서버로부터 응답으로 많은 양의 데이터 정보를 받게 된다면

분명히 한 화면의 텍스트뷰가 넘칠 것이다.

그래서 데이터 정보가 나타나는 이 텍스트뷰는

스크롤뷰안에 있어야 한다.

 

그래야 텍스트뷰 안에 데이터 정보가 넘치더라도 

사용자가 스크롤을 해서 아래까지 정보를 볼 수 있다.

 

activity_main.xml

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?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">
 
 
   <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="horizontal" >
 
       <EditText
           android:id="@+id/edt"
           android:layout_width="250dp"
           android:layout_height="wrap_content" />
 
       <Button
           android:id="@+id/btn"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="요청하기"
           android:textSize="30sp" />
 
   </LinearLayout>
 
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#00BCD4">
 
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="18sp" />
    </ScrollView>
</LinearLayout>
cs

화면까지 만들었으면 이제는 기능을 구현할 차례다.

 

위에서 언급했듯이 요청(Request) 객체를 생성해서

요청 큐(Request Que)에 추가(add()) 시켜주는 것에 집중하면 된다.

그러면 요청 큐가 알아서 처리해주니까.

 

MainActivity.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
 public class MainActivity extends AppCompatActivity {
 
   TextView tv;
   EditText edt;
    static RequestQueue requestQueue; // 요청 큐
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
       tv= (TextView)findViewById(R.id.tv);
       edt= (EditText)findViewById(R.id.edt);
 
       Button btn = (Button)findViewById(R.id.btn);
       btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendRequest();
            }
        });
 
        if(requestQueue == null) { // 여러개 만들어지면 안 좋기 때문에 없을 때만 생성
            requestQueue = Volley.newRequestQueue(getApplicationContext()); // 요청 큐 객체 생성
        }
    }
 
    public void sendRequest() { // Volley로 요청
        String url = editText.getText().toString(); // 링크를 EditText에 입력해서 url에 대입
 
        StringRequest request = new StringRequest( // 요청을 주고 받기 위한 StringRequest 객체 생성
                Request.Method.GET, // 웹 서버 요청 시 GET 방식이냐 POST 방식이냐를 구분함
                url, // EditText에 입력한 링크에다 요청
                new Response.Listener<String>() { // 응답 받을 리스너 객체를 전달, 응답을 문자열로 받아서 여기에다 넣음
                    @Override
                    public void onResponse(String response) {
                        println("응답 -> " + response);
                    }
                    },
                    new Response.ErrorListener() { // 응답에 오류가 있을 시 자동으로 호출
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            println("에러 -> " + error.getMessage());
                        }
 
                }
        ){
            @Override // POST 방식을 사용하면
            protected Map<StringString> getParams() throws AuthFailureError {
                Map<StringString> params = new HashMap<StringString>();
                return params; // 파라미터를 params 안에 추가해주면 요청 파라미터로 동작함, 이거를 요청 큐에 추가할거임
            }
        };
        request.setShouldCache(false); // 이전 결과가 있더라도 새로 요청해서 응답을 보여줘라
        requestQueue.add(request); // 요청 큐에 요청 객체를 넣으면 요청 큐가 알아서 요청과 응답 과정을 진행함
        println("요청 보냄");
    }
 
    public void println(String data) {
       tv.append(data + "\n"); // 한 줄씩 추가
    }
}
cs

Volley는 한번 제대로 알아두면 웹에서의 데이터를 가져오는데 도움이 된다.

 

피드백은 언제나 환영입니다.

 

 

반응형
반응형

우리가 앱을 이용할 때 화면이 스크롤 되는 것을 자주 볼 수 있다.

이는 화면의 많은 데이터들을 효율적으로 볼 수 있게 하는 중요한 기능이라고 할 수 있다.

 

그냥 xml파일에다가 스크롤뷰만 추가하면 된다.

 

ScrollView : 기본적으로 수직방향

HorizontalScrollView : 수평방향

 

1
2
3
4
5
6
7
<ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        
        
    </ScrollView>
cs

 

하지만 주의할 사항이 있다.

스크롤뷰 안에는 무조건 하나의 자식(뷰, 위젯)만 들어가 있어야한다.

그렇기 때문에 스크롤뷰안에는 하나의 큰 레이아웃으로 감싸서

그 안에 다른 레이아웃이나 위젯들을 넣어야한다.

 

정말 간단하지만 중요한 스크롤뷰 기능을 알아보았다.

 

피드백은 언제나 환영입니다.

 

 

반응형
반응형

(210518 수정)

 

보통 상하좌우 어디서든 나타나게 할 수 있는 바로가기 메뉴처럼

데이터들을 화면에 좀 더 효율적이고 동적으로 UI를 보여줘야 할 때가 있다.

 

이번에 알아볼 페이지 슬라이딩(Page Sliding)은

버튼을 눌렀을 때 보이지 않던 뷰가 슬라이딩 방식으로 미끄러지면서 나타나는 기능으로써

여러 뷰를 전환하면서 보여주는 프레임 레이아웃(Frame Layout) 방식에

애니메이션(Animation) 기능을 합친 것이다.

 

간단하기 때문에 쉽게 기능을 구현할 수 있다.

 

----------------------------------------------------------------------------------------------------

 

먼저 메인 레이아웃과 슬라이딩 레이아웃을 보여줄 activity_main.xml 파일부터 보겠다.

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#ff11ff11" >
        <!-- 첫 번째 레이아웃은 메인 레이아웃,
        background 색상은 본인 자유 -->
 
        <TextView
            android:layout_width="184dp"
            android:layout_height="match_parent"
            android:gravity="center"
            android:text="메인"
            android:textColor="#000000"
            android:textSize="60sp" />
        <!-- 메인 레이아웃에서 "메인"이라는
        텍스트를 표시해줌-->
 
    </LinearLayout>
 
    <LinearLayout
        android:id="@+id/sliding"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_gravity="right"
        android:background="#ffffff99"
        android:visibility="invisible">
        <!-- 두 번째 레이아웃은 슬라이딩으로 보여질 레이아웃,
        여기서 visibility가 가장 중요한데 평소에 안보였다가(invisible)
        오픈 버튼을 누르면 보이게(visible)할것임 -->
 
        <TextView
            android:layout_width="172dp"
            android:layout_height="match_parent"
            android:text="슬라이딩"
            android:textColor="#000000"
            android:textSize="40sp"
            android:gravity="center"/>
        <!-- 슬라이딩 레이아웃에서 "슬라이딩" 이라는
        텍스트를 표시함 -->
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="right|bottom" >
        <!-- 마지막 레이아웃은 버튼이 들어있는 레이아웃,
        버튼 위치는 layout_gravityfh로 설정할 수 있는데, 
        본인 자유이고 여기서는 오른쪽과 아래를 둘 다 사용함 -->
 
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="열기"
            android:textSize="50dp"
            android:layout_margin="20dp"/>
        <!-- 열기라는 버튼을 만들어서 누르면 슬라이딩
        레이아웃이 보이게함-->
 
    </LinearLayout>
</FrameLayout>
cs

 

이렇게 레이아웃들을 만들었고

다음으로는 슬라이딩 레이아웃이 움직이는 애니메이션 동작 정의를 할 것이다.

 

app밑에 res폴더 안에 anim 폴더를 만들고 

그 안에 sliding_left.xml과 sliding_right.xml 파일을 생성한다.

 

sliding_left.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
 
    <translate
        android:fromXDelta="100%p"
        android:toXDelta="0%p"
        android:duration="500" />
</set>
cs

 sliding_right.xml 

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
 
    <translate
        android:fromXDelta="0%p"
        android:toXDelta="100%p"
        android:duration="500" />
</set>
cs

 

여기서 <translate> 태그는 슬라이딩 레이아웃이 

왼쪽이나 오른쪽으로 이동하는 애니메이션을 정의한다.

 

fromXDelta 는 어디로부터 이동하냐 라는 말이라서

left파일 같은 경우에는 끝인 100%p 이고,

right파일 같은 경우에는 처음인 0%p 이다.

 

toXDelta 는 어디까지 이동하냐 라는 말이라서

left파일 같은 경우에는 처음인 0%p 이고,

right파일 같은 경우에는 끝인 100%p 이다.

 

duration은 동작하는 시간을 말하는데 1/1000초 단위이다. (500이면 0.5초)

 

UI를 했다면 이제는 마지막으로 동작시킬 코드가 필요하다.

 

MainActivity.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class MainActivity extends AppCompatActivity {
 
  LinearLayout sliding; // 애니메이션을 동작시킬 객체
   Animation slidingLeft; // 슬라이딩이 왼쪽으로 펼쳐지는 애니메이션 객체
   Animation slidingRight; // 슬라이딩이 오른쪽으로 접어지는 애니메이션 객체
  Button btn;
  boolean isOpen = false// 페이지가 처음에는 오픈이 안 된 상태
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
       sliding = (LinearLayout) findViewById(R.id.sliding);
       slidingLeft= AnimationUtils.loadAnimation(this, R.anim.sliding_left);
       slidingRight= AnimationUtils.loadAnimation(this, R.anim.sliding_right);
 
       SlidingAnimationListener listener = new SlidingAnimationListener();
       slidingLeft.setAnimationListener(listener);
       slidingRight.setAnimationListener(listener);
 
       btn = (Button)findViewById(R.id.btn);
       btn.setOnClickListener(new View.OnClickListener() { // 열기 버튼을 누르면
            @Override
            public void onClick(View v) {
                if (isOpen){ // 슬라이딩 레이아웃이 열려져 있으면
                   sliding.startAnimation(slidingRight); // 슬라이딩 레이아웃 닫기
                } else { // 슬라이딩 레이아웃이 닫혀져 있으면
                    sliding.setVisibility(View.VISIBLE); // 슬라이딩 레이아웃을 보이게하기
                   sliding.startAnimation(slidingLeft); // 슬라이딩 레이아웃 열기
                }
            }
        });
    }
 
    class  SlidingAnimationListener implements Animation.AnimationListener {
        @Override
        public void onAnimationStart(Animation animation) {
 
        }
 
        @Override
        public void onAnimationEnd(Animation animation) { // 애니메이션이 끝날 때 자동 호출됨
            if(isOpen) { // 슬라이딩 레이아웃의 열린 상태가 끝나면
                sliding.setVisibility(View.INVISIBLE); // 슬라이딩 레이아웃 안보이게 하고
                btn.setText("열기"); // 버튼에 있는 텍스트를 열기로 하기
                isOpen = false// 닫기 상태가 됨
            } else { // 슬라이딩 레이아웃의 닫힌 상태가 끝나면
                btn.setText("닫기"); // 버튼에 있는 텍스트를 닫기로 하기
                isOpen = true// 열기 상태가됨
            }
        }
 
        @Override
        public void onAnimationRepeat(Animation animation) {
 
        }
    }
}
cs

 

 

이렇게 페이지 슬라이딩(Page Sliding) 기능을 간단하게 알아보았다.

 

슬라이딩 레이아웃 닫혔을 때
슬라이딩 레이아웃 열렸을 때

 

 

 

피드백은 언제나 환영입니다. 

반응형
반응형

(210518 수정)

 

리사이클러뷰(RecyclerView)는 안드로이드 개발을 할 때에 굉장히 많이 사용된다.

왜냐하면 가장 많이 사용되는 UI의 모양들 중 하나이기 때문이다.

이전에 게시했던 채팅앱만 봐도 리사이클러뷰로 구현했다는 것을 알 수 있을 것이다.

 

기본적으로 사용되는 ListView와 비슷하지만 ListView의 기능에 추가적으로

레이아웃 설정도 자유롭고(유연성 증가), 효율성도 더 좋다(성능 발전).

 

RecyclerView가 어떤 것인지 간단하게 알아보고

이에 대한 구현 방법을 알아보겠다.

 

우선 리사이클러뷰(RecyclerView)란 무엇인가?

 

첫 번째,

언뜻 보면 비슷해보이지만

RecyclerView가 ListView와 다른 점은 재활용, 즉 재사용을 할 수 있다는 뷰라는 점에서

차이점을 보인다. 재사용을 하면 어떻게 다를까?

 

이전에 올린 채팅앱을 예시로 설명해보자.

예를 들어

100개의 채팅 메시지가 있고 화면에는 10개의 채팅 메시지만 보인다.

(채팅앱에서의 뷰 객체라고 하면 보통 말풍선을 나타내는 아이템 뷰이다.)

 

여기서 ListView는,

사용자가 스크롤을 위아래로 움직이면 맨 위에 있는 뷰 객체가 삭제되고

맨 아래에 새롭게 뷰 객체가 생성되는 것을 수 십번 수 백번 그 이상의 갱신을 반복하게 된다.

이렇게 되면 결과적으로

성능을 저하시키게 되는 요인이 된다.

 

하지만 RecyclerView는?

맨 위의 뷰 객체를 삭제하지 않고 맨 아래로 이동시켜

재사용(Recycle)을 한다.

하지만 뷰 객체 자체를 재사용하는 것일뿐,

뷰 객체가 가지고 있는 데이터는 새롭게 만들어진다.

 

화면에 10개의 채팅 메시지만 보이면 되기 때문에

몇 백 몇 천개의 채팅 메시지 데이터가 있든 말든

결국 뷰 객체는 10개만 있으면 된다.

 

두 번째,

레이아웃을 보다 유연하게 설정할 수 있다.

수직으로만 가능했던 ListView 한계점을 극복하고

RecyclerView에서는 수평도 가능하다.

 

이 부분은 LayoutManager라는 객체를 사용해서 설정하는데,

아래 코드들에서 다뤄볼 것이다.

 

-----------------------------------------------------------------------------------------

 

이제부터 RecyclerView를 간단하게 구현하면서 알아보자.

 

이번에는 카카오톡 같은 채팅앱의 채팅창 화면이 아닌

채팅할 친구목록을 구현해 볼 것이다.

 

먼저, 메인 친구목록(RecyclerView) 화면을 보여주는 activity_main.xml부터 작성해보자.

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?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" >
 
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
 
</LinearLayout>
cs

간단하게 RecyclerView만 구현해볼 것이기 때문에 (아직은 껍데기 상태의) RecyclerView만 넣는다.

 

그리고 친구목록을 보여주는 각 친구의 간단프로필을 list_item.xml로 만들건데,

이것은 어댑터의 인플레이션을 통해 뷰 객체가 될 것이다. 

 

list_item.xml

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="@drawable/item_line">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
 
        <ImageView
            android:id="@+id/iv"
            android:layout_width="77dp"
            android:layout_height="match_parent" />
 
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_weight="1">
 
            <TextView
                android:id="@+id/tvName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="이름"
                android:textSize="40sp" />
 
            <TextView
                android:id="@+id/tvStmsg"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="상태 메시지"
                android:textSize="25sp" />
            
        </LinearLayout>
    </LinearLayout>
</LinearLayout>
cs

 

아이템에 테두리를 만들어주고 코너를 둥글게 한다.

 

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
 
    <stroke android:width="1dp" />
 
    <corners
        android:radius="15dp" />
</shape>
cs

</shape>

친구의 간단프로필을 보여줄 아이템

그 다음으로는 친구목록의 데이터 정보를 담고 있는 Friends.java 라는 모델을 만든다.

Friends.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.example.recyclerviewtest;
 
public class Friends {
    String name; // 이름
    String stateMsg; // 상태메시지
    int resId; // 이미지 아이디
 
    public Friends(String name, String stateMsg, int resId) {
        this.name = name;
        this.stateMsg = stateMsg;
        this.resId = resId;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getStateMsg() {
        return stateMsg;
    }
 
    public void setStateMsg(String stateMsg) {
        this.stateMsg = stateMsg;
    }
 
    public int getResId() {
        return resId;
    }
 
    public void setImageView(int resId) {
        this.resId = resId;
    }
}
 
cs
 
 
 

 

처음에 만들었던 activity_main.xml에서의 RecyclerView는 껍데기에 불과하다.

이 RecyclerView를 실제로 화면에 보여지게 하려면 

어댑터(Adapter)를 통해서 데이터와 연결을 해야한다.

 

FriendsAdapter.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class FriendsAdapter extends RecyclerView.Adapter<FriendsAdapter.ViewHolder> {
 
    ArrayList<Friends> items = new ArrayList<Friends>(); // 아이템 리스트, 여러 아이템을 어댑터가 관리해야하기 때문에 어레이 리스트형 변수 선언
 
    @NonNull
    @Override // 리사이클러뷰에서 보여지는 여러 개의 아이템은 내부에서 캐시되기 때문에 아이템 갯수만큼 객체가 만들어지는 것은 아님
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());// 인플레이션을 하려면 Context 객체가 필요한데 뷰 그룹 객체의 getContext()를 이용하면 Context 참조 가능함함
       View itemView = inflater.inflate(R.layout.list_item, parent, false); // 뷰 그룹 객체는 각 아이템 뷰를 위한 뷰 그룹 객체이므로 xml을 인플레이션하여 이 뷰그룹 객체에 설정
 
        return new ViewHolder(itemView); // 뷰홀더 객체를 생성하면서 뷰 객체를 전달하고 그 뷰홀더 객체를 반환
    }
 
    @Override // 뷰홀더가 재사용될 때 호출되므로 뷰 객체는 기존 것을 그대로 사용하고 데이터만 바꿔줌
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Friends item = items.get(position); // 재활용 가능한 뷰홀더 객체를 파라미터로 전달하기 때문에 그 뷰홀더에 맞는 현재 아이템에 맞는 데이터만 설정(현재 몇 번째 것이 보여야 되는 시점인가)
        holder.setItem(item); // 핸들러에 아이템을 설정
    }
 
    @Override
    public int getItemCount() { // 어댑터가 관리하는 아이템의 갯수를 알아야 할 때 사용
        return items.size();
    }
 
    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;
        TextView tvStmsg;
       ImageView iv;
 
        public ViewHolder(@NonNull View itemView) { // 뷰홀더 생성자로 전달되는 뷰 객체를 참조함(아이템들은 뷰로 만들어지고, 뷰는 뷰홀더에 담아둠)
            super(itemView); // 이 뷰 객체를 부모 클래스의 변수에 담아둠
 
           // 뷰 객체에 들어 있는 텍스트뷰를 참조함
            tvName = itemView.findViewById(R.id.tvName);
            tvStmsg = itemView.findViewById(R.id.tvStmsg);
           iv= itemView.findViewById(R.id.iv);
        }
 
        public void setItem(Friends item) {
            tvName.setText(item.getName());
            tvStmsg.setText(item.getStateMsg());
           iv.setImageResource(item.getResId());
        }
 
    }
 
    // 이 어댑터가 아이템을 위한 Friends 객체를 어레이 리스트 안에 넣어 관리하기 때문에
    // 이 어댑터를 사용하는 소스코드에서 어댑터에 Friends 객체를 넣거나 가져갈 수 있도록 아래의 메소드들을 추가
 
    public void addItem(Friends item) {
        items.add(item); // 어레이 리스트에다 아이템 추가
    }
 
    public void setItems(ArrayList<Friends> items) {
        this.items = items;
    }
 
    public void getItem(int position) {
        items.get(position);
    }
 
    public void setItem(int position, Friends item) {
        items.set(position, item);
    }
}
 
cs}

 

어댑터를 통해서 아이템 뷰와 데이터를 연결했다면

MainActivity 클래스에서

어댑터의 addItem() 메서드를 이용해 Friends 객체를 추가한다.

 

MainActivity.java

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
 
        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); // 리니어 레이아웃 매니저를 생성해서 레이아웃 방향을 설정함(vertical)
        recyclerView.setLayoutManager(layoutManager); // 리사이클러뷰에 레이아웃 매니저 객체 설정-> 리니어 레이아웃으로 설정됨
        FriendsAdapter adapter = new FriendsAdapter(); // 리사이클러뷰와 어댑터 상호작용
 
        // 어댑터에 아이템을 추가함(Friends 객체의 이름, 상태 메시지, 프로필 이미지)
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
        adapter.addItem(new Friends("하빌리즘""안녕하세요", R.drawable.flower));
        adapter.addItem(new Friends("비둘기""gugugu", R.drawable.gugu));
 
        recyclerView.setAdapter(adapter); // 리사이클러뷰에 어댑터를 설정함
 
    }
}
cs}

최종 실행 화면

 

간단하게 RecyclerView에 대해서 알아보았는데

개인적으로 많은 공부가 되었고,

보시는 분들에게 조금이나마 도움이 되셨으면 좋겠다.

 

피드백은 언제나 환영입니다.

반응형

+ Recent posts