푸시알림이 아닌 앱 자체에서 알림을 발생


시키고 싶을 땐 Notification을 사용하면 


됩니다.


다음 코드를 통해 간단하게 작성가능합니다.




NotificationManager notificationManager;


PendingIntent intent;



먼저 NotificationMangerPendingIntent 객체를 하나씩 선언해줍니다.


NotificationManger는 notification(상태알림창의 알림)을 사용하기 위한 객체이며,


PendingIntent는 notification을 상태알림창에 띄울 intent를 뜻합니다.



가장 간단한 테스트로 onCreate 함수 내에 코드를 작성했습니다.


PendingIntent 객체인 intent는 다음과 같이 정의합니다.



intent = PendingIntent.getActivity(this, 0, new Intent(getApplicationContext(), MainActivity.class),

PendingIntent.FLAG_UPDATE_CURRENT);



Notification 내용을 설정하기 위해 Notifiaction.Builder를 이용하여 빌더를 만들어줍니다.



Notification.Builder builder = new Notification.Builder(this)

                .setSmallIcon(R.drawable.ic_launcher_background) // 아이콘 설정하지 않으면 오류남

                .setDefaults(Notification.DEFAULT_ALL)

                .setContentTitle("알림 제목") // 제목 설정

                .setContentText("알림 내용") // 내용 설정

                .setTicker("한줄 출력") // 상태바에 표시될 한줄 출력

                .setAutoCancel(true)

                .setContentIntent(intent);



제목, 내용 등을 다양하게 설정할 수 있습니다. 


이상하게도 아이콘을 설정해 주지않으면 오류가 나오는것을 확인할 수 있었습니다.


그래서 기본 아이콘 아무거나 가져와서 사용했습니다....



다음은 notificationManger 변수를 다음과 같이 정의해줍니다.



notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

notificationManager.notify(0, builder.build());



자 이것으로 알림(Notification) 기능 설정이 모두 끝났습니다.


테스트 코드는 onCreate함수에 모두 집어넣었기 때문에 프로그램 실행 시 바로 알림이 


뜨는것을 확인할 수 있었습니다.





Notification 예제 - 전체 코드입니다.


import android.app.Notification;

import android.app.NotificationManager;

import android.app.PendingIntent;

import android.content.Intent;

import android.os.Build;

import android.support.annotation.RequiresApi;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;


public class MainActivity extends AppCompatActivity {

    NotificationManager notificationManager;

    PendingIntent intent;


    @RequiresApi(api = Build.VERSION_CODES.M)

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        intent = PendingIntent.getActivity(this, 0,

                new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);


        Notification.Builder builder = new Notification.Builder(this)

                .setSmallIcon(R.drawable.ic_launcher_background) // 아이콘 설정하지 않으면 오류남

                .setDefaults(Notification.DEFAULT_ALL)

                .setContentTitle("알림 제목") // 제목 설정

                .setContentText("알림 내용") // 내용 설정

                .setTicker("한줄 출력") // 상태바에 표시될 한줄 출력

                .setAutoCancel(true)

                .setContentIntent(intent);


        notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        notificationManager.notify(0, builder.build());

    }

}





이상 '안드로이드 notification 사용법'에 대해 알아보았습니다.


질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!


공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.







안드로이드 앱을 개발하다보면 푸시알림 기능이 필요한 부분이 존재합니다.

기존의 GCM(Google Cloud Messaging) 서비스가 대표적이었는데, 최근 구글에서 

Firebase를 인수, FCM(Firebase Cloud Messaging) 서비스를 무료로 제공하고 있습니다.

현재 알려진 바로는 2019년까지 GCM 서비스를 제공하고 이후에는 모든 프로그램을 

FCM 서비스로 변경하라고 하네요!

자 그럼 이제 본격적으로 FCM 서비스를 사용해 봅시다.

먼저 FCM 서비스를 사용하려면 Firebase Console에 접속해야합니다.


접속하기 ☜ 클릭!

Firebase Console에 접속하면 아래와 같은 화면이 등장합니다.



프로젝트 추가를 눌러봅시다.



다음과 같이 화면이 나오면 프로젝트 이름을 기입하고, 약관에 동의를 하면 프로젝트 

만들기 버튼이 활성화됩니다. 프로젝트 만들기 버튼을 눌러 프로젝트를 생성합시다.

프로젝트 생성이 완료되면 새로운 화면이 나오는데 우측에 카테고리를 보시면 다음과 

같습니다.



우측 카테고리 메뉴 중 성장 - Cloud Messaging 을 선택합니다.





위 화면에서 iOS(아이폰), 안드로이드 중 원하는 버튼을 선택합니다.

본 포스팅은 안드로이드 기준으로 작성되었기 때문에 안드로이드를 선택했습니다.



안드로이드를 선택하면 다음과 같이 Android 앱에 Firebase를 추가하라고 나옵니다.

Android 패키지 이름을 작성해야하는데, 여기서 패키지 이름은 안드로이드 프로젝트 

생성시 설정한 패키지 이름을 작성합니다.

만약 패키지 이름을 모르시는 분은 프로젝트를 켜고 MainActivity로 들어갑니다.



그럼 가장 위에 package라고 적힌 부분이 있습니다.

이 부분이 이 프로젝트의 패키지 명입니다.

해당 부분을 복사해서 붙여넣기합니다.

그리고 나머지는 선택사항이므로 빈 칸으로 남겨두고 앱등록을 눌러줍니다.


앱등록이 끝나면 구성파일을 다운로드받습니다.

다운로드 받은 google-services.json 파일을 아래 사진과 같이 app 프로젝트 아래에

붙여넣기합니다.



프로젝트 폴더의 build.gradle의 dependencies에는 다음과 같은 코드를 추가합니다.

classpath 'com.google.gms:google-services:4.0.1'



그리고 app 프로젝트의 build.gradle의 dependencies 내에 다음과 같이 추가합니다.


implementation 'com.google.firebase:firebase-messaging:12.0.1'



그리고 나서 Sync Now버튼을 꼭 눌러주도록 합시다!


위의 과정을 모두 따라하셨다면 아래와 같이 앱에 Firebase를 성공적으로 추가했다는

화면이 나옵니다.



그리고 다시 안드로이드 스튜디오 프로젝트로 돌아와서 java 폴더 아래 패키지 폴더에 

Java Class 두개를 만들어줍니다.



첫번째는 FirebaseInstanceIDService입니다. 다음과 같이 작성합니다.

* 클래스명을 동일하게 만들어주세요


import android.util.Log;

import com.google.firebase.iid.FirebaseInstanceId;

import com.google.firebase.iid.FirebaseInstanceIdService;



public class FirebaseInstanceIDService extends FirebaseInstanceIdService {

    private static final String TAG = "MyFirebaseInstanceIDService";


    @Override

    public void onTokenRefresh() {

        String refreshedToken = FirebaseInstanceId.getInstance().getToken();

        Log.d(TAG, "Refreshed token: " + refreshedToken);

        sendRegistrationToServer(refreshedToken);

    }


    private void sendRegistrationToServer(String token) {

    }

}


두번째는 FireBaseMessagingService입니다. 다음과 같이 작성합니다.

* 클래스명을 동일하게 만들어주세요


import android.app.NotificationChannel;

import android.app.NotificationManager;

import android.app.PendingIntent;

import android.content.Intent;

import android.media.RingtoneManager;

import android.net.Uri;

import android.os.Build;

import android.os.Vibrator;

import android.support.v4.app.NotificationCompat;

import android.util.Log;


import com.google.firebase.messaging.FirebaseMessagingService;

import com.google.firebase.messaging.RemoteMessage;


public class FireBaseMessagingService extends FirebaseMessagingService {

    private static final String TAG = "MyFirebaseMsgService";


    @Override

    public void onMessageReceived(RemoteMessage remoteMessage) {

        Log.d(TAG, "From: " + remoteMessage.getFrom());


        if (remoteMessage.getData().size() > 0) {

            Log.d(TAG, "Message data payload: " + remoteMessage.getData());


            if (true) {

            } else {

                handleNow();

            }

        }

        if (remoteMessage.getNotification() != null) {

            Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());

            sendNotification(remoteMessage.getNotification().getBody());

        }

    }

    private void handleNow() {

        Log.d(TAG, "Short lived task is done.");

    }


    private void sendNotification(String messageBody) {

        Intent intent = new Intent(this, MainActivity.class);

        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,

                PendingIntent.FLAG_ONE_SHOT);


        String channelId = getString(R.string.default_notification_channel_id);

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        NotificationCompat.Builder notificationBuilder =

                new NotificationCompat.Builder(this, channelId)

                        .setSmallIcon(R.mipmap.ic_launcher)

                        .setContentTitle("FCM Message")

                        .setContentText(messageBody)

                        .setAutoCancel(true)

                        .setSound(defaultSoundUri)

                        .setContentIntent(pendingIntent);


        NotificationManager notificationManager =

                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            String channelName = getString(R.string.default_notification_channel_name);

            NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);

            notificationManager.createNotificationChannel(channel);

        }

        notificationManager.notify(0, notificationBuilder.build());

    }

}


위와 같이 클래스를 만들어주셨다면 AndroidManifest.xml로 이동합니다.

activity가 끝나는 부분 </activity> 바로 밑에 다음과 같이 작성합니다.




<service

    android:name=".FireBaseMessagingService">

    <intent-filter>

        <action android:name="com.google.firebase.MESSAGING_EVENT"/>

    </intent-filter>

</service>


<service

    android:name=".FirebaseInstanceIDService">

    <intent-filter>

        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>

    </intent-filter>

</service>


자 여기까지 안드로이드 프로젝트내 모든 설정이 끝났습니다.

이제 푸시알람이 제대로 가는지 확인해야겠죠?


다시 Firebase Console로 돌아갑니다. 새 메시지를 선택하고 다음과 같이 작성합니다.

메시지 내용을 작성하고 대상을 앱의 패키지를 선택합니다.

그리고 메시지보내기 버튼을 누르면!!



짠!

다음과 같이 상태 알림창에서 푸시 알림이 오는것을 확인 할 수 있습니다.



또한 안드로이드 프로젝트 Run 화면에서 Log에 찍히는걸 확인하실 수 있습니다.




이상 '안드로이드 FCM 푸시알림'에 대해 알아보았습니다.

질문 또는 오타나 잘못된 정보가 있는 경우 댓글로 달아주세요!

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.








안녕하세요 열코입니다.


이번에는 안드로이드에서 블루투스 연동하는 방법에 대해 알아보겠습니다.


먼저 안드로이드 프로젝트를 빈 프로젝트로 생성합니다.


그리고 안드로이드에서 블루투스에 관한 권한을 얻기 위해


app - src - main - AndroidManifest.xml에 다음과 같이 추가합니다.



1. AndroidManifest.xml에 추가


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

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


- BLUETOOTH : 블루투스 연결 요청, 연결 수락 및 데이터 전송과 같은 블루투스 통신을 수행하기 위해 선언합니다.


- BLUETOOTH_ADMIN : 블루투스 설정 조작 및 앱에서 기기를 검색하기 위해 선언합니다.



2. activity_main.xml에 추가


매인 화면의 액티비티는 다음과 같이 구성되어 있습니다.


<LinearLayout

    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">


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="수신 받은 데이터 : "/>


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:id="@+id/textView_receive"/>


    </LinearLayout>


    <LinearLayout

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">


        <TextView

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:text="송신 할 데이터 : "/>


        <EditText

            android:layout_width="wrap_content"

            android:layout_height="wrap_content"

            android:id="@+id/editText_send"

            android:ems="10"/>


    </LinearLayout>


    <Button

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:text="데이터 송신"

        android:id="@+id/button_send"/>

</LinearLayout>



3. MainActivity.java에 추가


* 함수들의 호출 구조가 약간 복잡하게 되어있습니다. 설명 하는 순서를 잘 따라와 주시기 바랍니다.


매인 액티비티 클래스 안에 다음과 변수들을 선언합니다. 변수들의 용도는 주석으로 설명돼있습니다.


private static final int REQUEST_ENABLE_BT = 10; // 블루투스 활성화 상태

private BluetoothAdapter bluetoothAdapter; // 블루투스 어댑터

private Set<BluetoothDevice> devices; // 블루투스 디바이스 데이터 셋

private BluetoothDevice bluetoothDevice; // 블루투스 디바이스

private BluetoothSocket bluetoothSocket = null; // 블루투스 소켓

private OutputStream outputStream = null; // 블루투스에 데이터를 출력하기 위한 출력 스트림

private InputStream inputStream = null; // 블루투스에 데이터를 입력하기 위한 입력 스트림

private Thread workerThread = null; // 문자열 수신에 사용되는 쓰레드

private byte[] readBuffer; // 수신 된 문자열을 저장하기 위한 버퍼

private int readBufferPosition; // 버퍼 내 문자 저장 위치


private TextView textViewReceive; // 수신 된 데이터를 표시하기 위한 텍스트 뷰

private EditText editTextSend; // 송신 할 데이터를 작성하기 위한 에딧 텍스트

private Button buttonSend; // 송신하기 위한 버튼


위 변수들을 모두 선언해주시고 onCreate 함수 안에 다음과 같이 작성합니다.



@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);


    // 각 컨테이너들의 id를 매인 xml과 맞춰준다.

    textViewReceive = (TextView)findViewById(R.id.textView_receive);

    editTextSend = (EditText)findViewById(R.id.editText_send);

    buttonSend = (Button)findViewById(R.id.button_send);


    // 블루투스 활성화하기

    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 블루투스 어댑터를 디폴트 어댑터로 설정


    if(bluetoothAdapter == null) { // 디바이스가 블루투스를 지원하지 않을 때

        // 여기에 처리 할 코드를 작성하세요.

    }

    else { // 디바이스가 블루투스를 지원 할 때

        if(bluetoothAdapter.isEnabled()) { // 블루투스가 활성화 상태 (기기에 블루투스가 켜져있음)

            selectBluetoothDevice(); // 블루투스 디바이스 선택 함수 호출                

        }

        else { // 블루투스가 비 활성화 상태 (기기에 블루투스가 꺼져있음)

            // 블루투스를 활성화 하기 위한 다이얼로그 출력

            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 

            // 선택한 값이 onActivityResult 함수에서 콜백된다.

            startActivityForResult(intent, REQUEST_ENABLE_BT); 

        }

    }

}


마찬가지로 간단한 설명은 모두 주석으로 처리했습니다.


onCreate 함수는 프로그램(App)이 실행되고 MainActivity가 자동으로 호출되며 호출되는 순간 onCreate 함수가 호출됩니다.


따라서 가장 먼저 호출되는 함수라고 생각하시면 됩니다. 이 함수안에 블루투스를 활성화 하는 코드를 작성했습니다.


해당 코드를 실행하면 다음과 같은 다이얼로그(대화상자)가 나오며 '사용'을 누르면 디바이스의 블루투스를 켜주는 코드가 되겠습니다.



만약 블루투스를 지원하지 않는 기기의 경우 본 프로그램이 정상 작동하지 않으니 꼭 디바이스가 블루투스를 지원하지 않을 때 < 조건 문에서 처리할 코드를 작성하시기 바랍니다.


☞ "예를들어 finish() 함수를 호출하여 프로그램을 종료시킬 수 있습니다."



권한 요청 다이얼로그에서 '사용'을 누르면 onActivityResult() 함수가 호출됩니다.



onActivityResult() 함수는 Alt + insert 키를 눌러 Override Methods를 눌러 onActivityResult를 선택하면 자동으로 생성됩니다.


생성된 onActivityResult() 함수에 다음과 같이 코드를 작성합니다.



@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

    super.onActivityResult(requestCode, resultCode, data);


    switch (requestCode) {

        case REQUEST_ENABLE_BT :

            if(requestCode == RESULT_OK) { // '사용'을 눌렀을 때

                selectBluetoothDevice(); // 블루투스 디바이스 선택 함수 호출

            }

            else { // '취소'를 눌렀을 때

                // 여기에 처리 할 코드를 작성하세요.

            }

            break;

    }

}


'사용'을 눌렀다면 selectBluetoothDevice() 함수를 호출합니다.


만약 '취소'를 눌렀다면 블루투스를 활성화 할 수 없기 때문에 처리 할 코드를 작성 해 주시면 됩니다.


여기까지 블루투스가 비활성화 돼있을 때 수행되는 코드였으며,



아래부터는 블루투스가 이미 활성화 돼있을 때 수행되는 코드입니다.


디바이스가 블루투스를 지원하며 블루투스가 이미 켜져있는 상태라면 selectBluetoothDevice() 함수를 호출합니다.


selectBluetoothDevice() 함수를 다음과 같이 작성합니다. 


public void selectBluetoothDevice() {

    // 이미 페어링 되어있는 블루투스 기기를 찾습니다.

    devices = bluetoothAdapter.getBondedDevices();

    // 페어링 된 디바이스의 크기를 저장

    pariedDeviceCount = devices.size();

    // 페어링 되어있는 장치가 없는 경우

    if(pariedDeviceCount == 0) {

        // 페어링을 하기위한 함수 호출

    }

    // 페어링 되어있는 장치가 있는 경우

    else {

        // 디바이스를 선택하기 위한 다이얼로그 생성

        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setTitle("페어링 되어있는 블루투스 디바이스 목록");

        // 페어링 된 각각의 디바이스의 이름과 주소를 저장

        List<String> list = new ArrayList<>();

        // 모든 디바이스의 이름을 리스트에 추가

        for(BluetoothDevice bluetoothDevice : devices) {

            list.add(bluetoothDevice.getName());

        }

        list.add("취소");


        // List를 CharSequence 배열로 변경

        final CharSequence[] charSequences = list.toArray(new CharSequence[list.size()]);

        list.toArray(new CharSequence[list.size()]);


        // 해당 아이템을 눌렀을 때 호출 되는 이벤트 리스너

        builder.setItems(charSequences, new DialogInterface.OnClickListener() {

            @Override

            public void onClick(DialogInterface dialog, int which) {

                // 해당 디바이스와 연결하는 함수 호출

                connectDevice(charSequences[which].toString());

            }

        });


        // 뒤로가기 버튼 누를 때 창이 안닫히도록 설정

        builder.setCancelable(false);

        // 다이얼로그 생성

        AlertDialog alertDialog = builder.create();

        alertDialog.show();

    }

}


페어링 되어있는 장치가 없는 경우 페어링을 직접 수행하는 함수를 호출해야합니다.


블루투스 페어링 하는 함수는 최대한 빨리 작성하겠습니다.


현재 프로그램은 이미 페어링 되어있는 블루투스 디바이스만 찾을 수 있습니다.





페어링 되어있는 장치가 있는경우 그 목록을 리스트로 만들어 다이얼로그에 띄웁니다.


해당 리스트 아이템을 눌렀을 때 해당 디바이스와 연결하는 함수(connectDevice 함수)를 호출합니다.


connectDevice() 함수는 다음과 같이 작성합니다.


public void connectDevice(String deviceName) {

    // 페어링 된 디바이스들을 모두 탐색

    for(BluetoothDevice tempDevice : devices) {

        // 사용자가 선택한 이름과 같은 디바이스로 설정하고 반복문 종료

        if(deviceName.equals(tempDevice.getName())) {

            bluetoothDevice = tempDevice;

            break;

        }

    }

    // UUID 생성

    UUID uuid = java.util.UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

    // Rfcomm 채널을 통해 블루투스 디바이스와 통신하는 소켓 생성

    try {

        bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);

        bluetoothSocket.connect();

        // 데이터 송,수신 스트림을 얻어옵니다.

        outputStream = bluetoothSocket.getOutputStream();

        inputStream = bluetoothSocket.getInputStream();

        // 데이터 수신 함수 호출

        receiveData();

    } catch (IOException e) {

        e.printStackTrace();

    }

}


이 함수는 위 다이얼로그에서 선택한 블루투스 디바이스와 연결하는 함수입니다.


블루투스를 연결하고 데이터를 수신하기 위한 함수 receiveData() 함수를 호출합니다.


receiveData() 함수는 다음과 같이 작성합니다.



public void receiveData() {

    final Handler handler = new Handler();

    // 데이터를 수신하기 위한 버퍼를 생성

    readBufferPosition = 0;

    readBuffer = new byte[1024];


    // 데이터를 수신하기 위한 쓰레드 생성

    workerThread = new Thread(new Runnable() {

        @Override

        public void run() {

            while(Thread.currentThread().isInterrupted()) {

                try {

                    // 데이터를 수신했는지 확인합니다.

                    int byteAvailable = inputStream.available();

                    // 데이터가 수신 된 경우

                    if(byteAvailable > 0) {

                        // 입력 스트림에서 바이트 단위로 읽어 옵니다.

                        byte[] bytes = new byte[byteAvailable];

                        inputStream.read(bytes);

                        // 입력 스트림 바이트를 한 바이트씩 읽어 옵니다.

                        for(int i = 0; i < byteAvailable; i++) {

                            byte tempByte = bytes[i];

                            // 개행문자를 기준으로 받음(한줄)

                            if(tempByte == '\n') {

                                // readBuffer 배열을 encodedBytes로 복사

                                byte[] encodedBytes = new byte[readBufferPosition];

                                System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);

                                // 인코딩 된 바이트 배열을 문자열로 변환

                                final String text = new String(encodedBytes, "US-ASCII");

                                readBufferPosition = 0;

                                handler.post(new Runnable() {

                                    @Override

                                    public void run() {

                                        // 텍스트 뷰에 출력

                                        textViewReceive.append(text + "\n");

                                    }

                                });

                            } // 개행 문자가 아닐 경우

                            else {

                                readBuffer[readBufferPosition++] = tempByte;

                            }

                        }

                    }

                } catch (IOException e) {

                    e.printStackTrace();

                }

                try {

                    // 1초마다 받아옴

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

        }

    });

    workerThread.start();

}


수신 된 데이터를 쓰레드를 통해 1초마다 바이트 배열로 받아 인코딩 후 스트링 변수로 변환 하여 텍스트 뷰에 출력하는 코드입니다.


여기까지 블루투스를 통한 데이터 수신 과정이었습니다.



아래는 블루투스를 통한 데이터 송신 과정에 대해 설명하겠습니다.


먼저 이전에 만든 버튼에 다음과 같은 버튼 리스너를 추가합니다. (위치는 onCreate안 buttonSend findViewbyId 밑에 만들면 됩니다.)


buttonSend.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

        sendData(editTextSend.getText().toString());

    }

});


버튼을 누르면 sendData() 함수를 호출하는 이벤트 리스너입니다.


sendData()함수를 다음과 같이 작성합니다.


void sendData(String text) {

    // 문자열에 개행문자("\n")를 추가해줍니다.

    text += "\n";

    try{

        // 데이터 송신

        outputStream.write(text.getBytes());

    }catch(Exception e) {

        e.printStackTrace();

    }

}


여기까지 잘 따라오셨다면 블루투스 데이터 송,수신 프로그램이 올바르게 작동 할 것입니다.




이상 안드로이드 블루투스 연동이었습니다.


부족한 부분이나 오류가 나는 부분, 기타 질문사항은 댓글로 남겨주세요!


공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아 뵙겠습니다.




저번에 Tesseract와 OpenALPR을 이용하여 C# 프로그램을 만들었는데 이번에는 안드로이드에서 만들어보겠습니다!!


참고 ☞ C# 문자인식 프로그래밍


안드로이드에서 문자인식을 하기위해서는 기본적으로 OpenCV 라이브러리가 필요합니다.


참고 ☞ 안드로이드에서 OpenCV 사용하기


안드로이드에서는 API를 사용하지 않고 조금 원시적인(?) 방법으로 문자인식으로 해보려고 합니다.


※ 문자인식을 하기 위한 절차


1. 안드로이드와 카메라를 연동합니다.(CameraPreview)

2. 사진을 촬영하고 내부저장소에 저장합니다.(PNG)

3. 저장소에 저장된 이미지파일을 불러옵니다.(Bitmap)

4. 불러온 이미지 파일에 전처리 작업을 수행합니다.(GrayScale, ThresHold, 등)

5. 전체 이미지에서 번호판 영역을 관심영역으로 설정하고 따로 분류합니다.(ROI, contours)

6. 분류된 번호판 영역 이미지 파일에서 번호판 숫자 및 문자를 라벨링 처리합니다.

7. 라벨링 처리 된 숫자 및 문자들을 미리 준비한 템플릿과 비교합니다.

8. 비교한 결과 중 가장 매칭률이 높은 숫자 및 문자를 선별합니다.

9. 모든 숫자와 문자를 순서대로 조합하면 문자인식 결과입니다.


안드로이드에서도 물론 Tesseract API(tess-two)를 사용할 수 있습니다. (30줄 이내에...) 

하지만 이런 원시적인 방법을 통해 실제로 문자 및 패턴인식을 하는 원리를 파악할 수 있으며 가장 중요한것은 인식률이 상당히 높다는 것입니다!!! (Tesseract OCR이 한글 문자 인식률이 매우 낮은걸로 유명하죠... 하지만 워낙 라이브러리 자체가 유연해서 거의 모든 프로그래밍 언어를 지원하며 문자 인식 훈련을 통해 인식률을 높일수 있답니다.)


자 그럼 절차대로 하나하나씩 따라해봅시다.


1, 2. 안드로이드 카메라 연동 및 사진 저장 : 이미 블로그에 설명한 내용이 있으므로 링크로 대체합니다.


3. 저장된 이미지 파일 불러오기 : 내부 저장소에 저장된 이미지 파일을 불러오는 방법은 다음과 같이 간단합니다. PNG파일을 File 객체를 통해 지정하고 BitmapFactory.decodeFile을 통해 이미지를 비트맵 객체로 불러올 수 있습니다.


File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/OpenCVTest/sample.png");

if(file.exists()){

Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());


4. 이미지 전처리 작업 : 기본적으로 GrayScale(흑백) 및 ThresHold(이진화) 처리를 수행합니다.


Bitmap image1;

OpenCVLoader.initDebug();


Mat img1=new Mat();

Utils.bitmapToMat(myBitmap ,img1);


Mat imageGray1 = new Mat();

Mat imageCny1 = new Mat();


Imgproc.cvtColor(img1, imageGray1, Imgproc.COLOR_BGR2GRAY);

Imgproc.threshold(imageGray1, imageCny1, 160, 255, Imgproc.THRESH_BINARY);


5. 이미지 관심영역 추출 : 링크


6, 7, 8, 9. 번호판 숫자 인식 : 

코드가 약간 복잡합니다. 간단히 설명드리면 다음 절차와 같이 수행합니다.

1. 전체사진 불러오기 -> 전처리 -> 번호판만 추출

2. 번호판사진 불러오기 -> 흑백 반전(검은바탕에 흰색 숫자가 되도록) -> 숫자 추출

3. 추출된 숫자와 템플릿 비교

4. 결과 출력


* 신형 번호판의 숫자와 문자는 00가 0000 이런식으로 숫자6개 문자1개 총 7개의 라벨이 생깁니다. 하지만 'ㄱ ㅏ' 라는 문자의 경우 'ㄱ'과 'ㅏ'를 따로 라벨링 처리하기 때문에 총 8개의 문자가 생길때도 있습니다. 이를 처리하기위해 라벨이 7개, 8개인 경우를 각각 조건문으로 처리했습니다.


int[][] listarr = new int[8][4];

List<MatOfPoint> contoursROI = new ArrayList<>();

Mat hierarchyROI = new Mat();

Mat matROI = new Mat();

Mat ResultROI = new Mat();

Utils.bitmapToMat(roi, matROI);

Imgproc.cvtColor(matROI, ResultROI, Imgproc.COLOR_BGR2GRAY, 1);

Imgproc.findContours(ResultROI, contoursROI, hierarchyROI, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

int count = 0;

for(int idx = 0; idx >= 0; idx = (int) hierarchyROI.get(0, idx)[0]) {

    if(count>7) break;

    MatOfPoint matOfPoint = contoursROI.get(idx);

    Rect rect = Imgproc.boundingRect(matOfPoint);

    // 해상도별로 조절하기.

    if(rect.x < roi.getWidth()/20 || rect.x > roi.getWidth()-(roi.getWidth()/10) ||

            rect.y < roi.getHeight()/10 || rect.y > roi.getHeight()-(roi.getHeight()/10) ||

            rect.width < roi.getWidth()/50 || rect.width > roi.getWidth()/8 ||

            rect.height <= roi.getHeight()/10 ) continue;


    Log.d("RECT : ", "x : " + rect.x + ", y : " + rect.y + ", w :" + rect.width + ", h : " + rect.height);

    Imgproc.rectangle(matROI, rect.tl(), rect.br(), new Scalar(255, 0, 0, 255), 1);


    listarr[count][0] = rect.x;

    listarr[count][1] = rect.y;

    listarr[count][2] = rect.width;

    listarr[count][3] = rect.height;

    count++;

}

if(count == 7) {

    // 오름차순 정렬

    for(int i = 0; i < 7; i++) {

        for(int j = i; j < 7; j++) {

            if(listarr[i][0] > listarr[j][0]) {

                // 스왑

                int[] temp = new int[4];

                temp[0] = listarr[i][0];

                temp[1] = listarr[i][1];

                temp[2] = listarr[i][2];

                temp[3] = listarr[i][3];

                listarr[i][0] = listarr[j][0];

                listarr[i][1] = listarr[j][1];

                listarr[i][2] = listarr[j][2];

                listarr[i][3] = listarr[j][3];

                listarr[j][0] = temp[0];

                listarr[j][1] = temp[1];

                listarr[j][2] = temp[2];

                listarr[j][3] = temp[3];

            }

        }

    }


    for(int i = 0; i < 7; i++) {

        Bitmap tempBitmap = Bitmap.createBitmap(roi, listarr[i][0], listarr[i][1], listarr[i][2], listarr[i][3]);

        tempBitmap = Bitmap.createScaledBitmap(tempBitmap, 10, 15, true);

        if(i == 0)

            imageViewROI1.setImageBitmap(tempBitmap);

        else if(i == 1)

            imageViewROI2.setImageBitmap(tempBitmap);

        else if(i == 2)

            imageViewROI3.setImageBitmap(tempBitmap);

        else if(i == 3)

            imageViewROI4.setImageBitmap(tempBitmap);

        else if(i == 4)

            imageViewROI5.setImageBitmap(tempBitmap);

        else if(i == 5)

            imageViewROI6.setImageBitmap(tempBitmap);

        else if(i == 6)

            imageViewROI7.setImageBitmap(tempBitmap);


        int num_count = 0;

        double max = 0.0;

        int num = 0;

        if(i==2) { // 문자

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(ones[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/proones[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }

        else { // 숫자

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(nums[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/pronums[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        avg+=max;

        String floatnum = String.format("%.2f", max);


        if(i == 2) { // 한글 처리

            // 중략


            }


            int a = 0, b = 0, c = 0;

            for(int x=0; x<tempBitmap.getHeight(); x++) {

                for (int y = 0; y < tempBitmap.getWidth(); y++) {

                    if (tempBitmap.getPixel(y, x) == -1) {

                        Log.d("arr", "ones[5][" + a + "][" + b + "] = " + tempBitmap.getPixel(y, x) + ";");

                        c++;

                    }

                    b++;

                }

                b = 0;

                a++;

            }

            Log.d("count" , ""+c);

        }

        else { // 숫자 처리

            textViewResult.append(i + "번째 분석 결과 : " + num + " / 정확도 : " + floatnum +"%\n");

            result += Integer.toString(num);

        }

    }

}

else if(count == 8) {

    // 오름차순 정렬

    for(int i = 0; i < 8; i++) {

        for(int j = i; j < 8; j++) {

            if(listarr[i][0] > listarr[j][0]) {

                // 스왑

                int[] temp = new int[4];

                temp[0] = listarr[i][0];

                temp[1] = listarr[i][1];

                temp[2] = listarr[i][2];

                temp[3] = listarr[i][3];

                listarr[i][0] = listarr[j][0];

                listarr[i][1] = listarr[j][1];

                listarr[i][2] = listarr[j][2];

                listarr[i][3] = listarr[j][3];

                listarr[j][0] = temp[0];

                listarr[j][1] = temp[1];

                listarr[j][2] = temp[2];

                listarr[j][3] = temp[3];

            }

        }

    }

    // 한글인 경우

    if(abs(listarr[2][0]-listarr[3][0]) < 20) {

        if(listarr[2][1] > listarr[3][1]) {

            // 스왑

            int[] temp = new int[4];

            temp[0] = listarr[2][0];

            temp[1] = listarr[2][1];

            temp[2] = listarr[2][2];

            temp[3] = listarr[2][3];

            listarr[2][0] = listarr[3][0];

            listarr[2][1] = listarr[3][1];

            listarr[2][2] = listarr[3][2];

            listarr[2][3] = listarr[3][3];

            listarr[3][0] = temp[0];

            listarr[3][1] = temp[1];

            listarr[3][2] = temp[2];

            listarr[3][3] = temp[3];

        }

    }


    for(int i = 0; i < 8; i++) {

        Bitmap tempBitmap = Bitmap.createBitmap(roi, listarr[i][0], listarr[i][1], listarr[i][2], listarr[i][3]);

        tempBitmap = Bitmap.createScaledBitmap(tempBitmap, 10, 15, true);

        if(i == 0)

            imageViewROI1.setImageBitmap(tempBitmap);

        else if(i == 1)

            imageViewROI2.setImageBitmap(tempBitmap);

        else if(i == 2)

            imageViewROI3.setImageBitmap(tempBitmap);

        else if(i == 3)

            imageViewROI4.setImageBitmap(tempBitmap);

        else if(i == 4)

            imageViewROI5.setImageBitmap(tempBitmap);

        else if(i == 5)

            imageViewROI6.setImageBitmap(tempBitmap);

        else if(i == 6)

            imageViewROI7.setImageBitmap(tempBitmap);

        else if(i == 7)

            imageViewROI8.setImageBitmap(tempBitmap);


        int num_count = 0;

        double max = 0.0;

        int num = 0;

        // 자음

        if(i==2) {

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(cons[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/procons[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        // 모음

        else if(i==3) {

            for(int n = 0; n < 4; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(vocs[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/provocs[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        // 숫자

        else {

            for(int n = 0; n < 10; n++) {

                for(int j = 0; j < 15; j++) {

                    for(int k = 0; k < 10; k++) {

                        if(nums[n][j][k] == tempBitmap.getPixel(k, j))

                            num_count++;

                    }

                }

                double result = (double)num_count/pronums[n]*100;

                if(result > 100) result = 100;

                if(result > max) {

                    max = result;

                    num = n;

                }

                num_count=0;

            }

        }


        avg+=max;

        String floatnum = String.format("%.2f", max);


        // 자음

        if(i==2) {

            // 중략

        }


        // 모음

        else if(i==3) {

            // 중략

        }


        // 숫자

        else {

            textViewResult.append(i + "번째 분석 결과 : " + num + " / 정확도 : " + floatnum +"%\n");

            result += Integer.toString(num);

        }

    }

}

else {

    Toast.makeText(context, "숫자 생성 실패", Toast.LENGTH_SHORT).show();

}


Utils.matToBitmap(matROI, roi2);


imageViewROI = (ImageView)findViewById(R.id.image_result_ROI);

imageViewROI.setImageBitmap(roi2);


String resultnum = String.format("%.2f", avg/count);

textViewResult.append("\n\n분석 결과 : " + result + " / 정확도 : " + resultnum + "%");

}


중간에 문자 비교 코드는 너무 길어서 중략 처리했습니다.

다음은 미리 저장해둔 문자 템플릿입니다.


nums = new int[10][15][10];

pronums = new int[10];


cons = new int[10][15][10];

procons = new int[10];


vocs = new int[4][15][10];

provocs = new int[4];


ones = new int[10][15][10];

proones = new int[10];


// 픽셀 개수

pronums[0] = 50;

pronums[1] = 40;

pronums[2] = 40;

pronums[3] = 45;

pronums[4] = 50;

pronums[5] = 55;

pronums[6] = 50;

pronums[7] = 37;

pronums[8] = 50;

pronums[9] = 40;


// 0

nums[0][0][3] = -1; nums[0][0][4] = -1; nums[0][0][5] = -1; nums[0][0][6] = -1;

nums[0][1][2] = -1; nums[0][1][3] = -1; nums[0][1][6] = -1; nums[0][1][7] = -1;

nums[0][2][0] = -1; nums[0][2][1] = -1; nums[0][2][8] = -1; nums[0][2][9] = -1;

nums[0][3][0] = -1; nums[0][3][1] = -1; nums[0][3][8] = -1; nums[0][3][9] = -1;

nums[0][4][0] = -1; nums[0][4][1] = -1; nums[0][4][8] = -1; nums[0][4][9] = -1;

nums[0][5][0] = -1; nums[0][5][1] = -1; nums[0][5][8] = -1; nums[0][5][9] = -1;

nums[0][6][0] = -1; nums[0][6][1] = -1; nums[0][6][8] = -1; nums[0][6][9] = -1;

nums[0][7][0] = -1; nums[0][7][1] = -1; nums[0][7][8] = -1; nums[0][7][9] = -1;

nums[0][8][0] = -1; nums[0][8][1] = -1; nums[0][8][8] = -1; nums[0][8][9] = -1;

nums[0][9][0] = -1; nums[0][9][1] = -1; nums[0][9][8] = -1; nums[0][9][9] = -1;

nums[0][10][0] = -1; nums[0][10][1] = -1; nums[0][10][8] = -1; nums[0][10][9] = -1;

nums[0][11][0] = -1; nums[0][11][1] = -1; nums[0][11][8] = -1; nums[0][11][9] = -1;

nums[0][12][0] = -1; nums[0][12][1] = -1; nums[0][12][8] = -1; nums[0][12][9] = -1;

nums[0][13][2] = -1; nums[0][13][3] = -1; nums[0][13][6] = -1; nums[0][13][7] = -1;

nums[0][14][3] = -1; nums[0][14][4] = -1; nums[0][14][5] = -1; nums[0][14][6] = -1;


// 1

nums[1][0][7] = -1; nums[1][0][8] = -1;

nums[1][1][6] = -1; nums[1][1][7] = -1; nums[1][1][8] = -1;

nums[1][2][1] = -1; nums[1][2][2] = -1; nums[1][2][3] = -1; nums[1][2][4] = -1; nums[1][2][5] = -1; nums[1][2][6] = -1; nums[1][2][7] = -1; nums[1][2][8] = -1;

nums[1][3][0] = -1; nums[1][3][1] = -1; nums[1][3][2] = -1; nums[1][3][3] = -1; nums[1][3][4] = -1; nums[1][3][5] = -1; nums[1][3][6] = -1; nums[1][3][7] = -1; nums[1][3][8] = -1;

nums[1][4][6] = -1; nums[1][4][7] = -1; nums[1][4][8] = -1;

nums[1][5][7] = -1; nums[1][5][8] = -1;

nums[1][6][7] = -1; nums[1][6][8] = -1;

nums[1][7][7] = -1; nums[1][7][8] = -1;

nums[1][8][7] = -1; nums[1][8][8] = -1;

nums[1][9][7] = -1; nums[1][9][8] = -1;

nums[1][10][7] = -1; nums[1][10][8] = -1;

nums[1][11][7] = -1; nums[1][11][8] = -1;

nums[1][12][7] = -1; nums[1][12][8] = -1;

nums[1][13][7] = -1; nums[1][13][8] = -1;

nums[1][14][7] = -1; nums[1][14][8] = -1;


// 2

nums[2][0][3] = -1; nums[2][0][4] = -1;

nums[2][1][1] = -1; nums[2][1][2] = -1; nums[2][1][5] = -1; nums[2][1][6] = -1;

nums[2][2][0] = -1; nums[2][2][1] = -1; nums[2][2][7] = -1;

nums[2][3][0] = -1; nums[2][3][1] = -1; nums[2][3][7] = -1; nums[2][3][8] = -1;

nums[2][4][7] = -1; nums[2][4][8] = -1;

nums[2][5][7] = -1; nums[2][5][8] = -1;

nums[2][6][6] = -1; nums[2][6][7] = -1;

nums[2][7][6] = -1; nums[2][7][7] = -1;

nums[2][8][5] = -1; nums[2][8][6] = -1;

nums[2][9][4] = -1; nums[2][9][5] = -1;

nums[2][10][3] = -1; nums[2][10][4] = -1;

nums[2][11][3] = -1; nums[2][11][4] = -1;

nums[2][12][2] = -1; nums[2][12][3] = -1;

nums[2][13][1] = -1; nums[2][13][2] = -1; nums[2][13][3] = -1;

nums[2][14][1] = -1; nums[2][14][2] = -1; nums[2][14][3] = -1; nums[2][14][4] = -1; nums[2][14][5] = -1; nums[2][14][6] = -1; nums[2][14][7] = -1; nums[2][14][8] = -1; nums[2][14][9] = -1;


// 3

nums[3][0][2] = -1; nums[3][0][3] = -1; nums[3][0][4] = -1; nums[3][0][5] = -1; nums[3][0][6] = -1; nums[3][0][7] = -1; nums[3][0][8] = -1; nums[3][0][9] = -1;

nums[3][1][3] = -1; nums[3][1][4] = -1; nums[3][1][5] = -1; nums[3][1][6] = -1; nums[3][1][7] = -1; nums[3][1][8] = -1; nums[3][1][9] = -1;

nums[3][2][7] = -1; nums[3][2][8] = -1;

nums[3][3][6] = -1; nums[3][3][7] = -1;

nums[3][4][5] = -1; nums[3][4][6] = -1;

nums[3][5][4] = -1; nums[3][5][5] = -1; nums[3][5][6] = -1;

nums[3][6][5] = -1; nums[3][6][6] = -1; nums[3][6][7] = -1;

nums[3][7][7] = -1;

nums[3][8][7] = -1; nums[3][8][8] = -1;

nums[3][9][7] = -1; nums[3][9][8] = -1;

nums[3][10][0] = -1; nums[3][10][1] = -1; nums[3][10][7] = -1;

nums[3][11][0] = -1; nums[3][11][1] = -1; nums[3][11][7] = -1;

nums[3][12][0] = -1; nums[3][12][1] = -1; nums[3][12][6] = -1;

nums[3][13][1] = -1; nums[3][13][2] = -1; nums[3][13][3] = -1; nums[3][13][4] = -1; nums[3][13][5] = -1; nums[3][13][6] = -1;

nums[3][14][2] = -1; nums[3][14][3] = -1; nums[3][14][4] = -1;


// 4

nums[4][0][6] = -1; nums[4][0][7] = -1;

nums[4][1][6] = -1; nums[4][1][7] = -1;

nums[4][2][5] = -1; nums[4][2][6] = -1; nums[4][2][7] = -1;

nums[4][3][4] = -1; nums[4][3][5] = -1; nums[4][3][6] = -1; nums[4][3][7] = -1;

nums[4][4][3] = -1; nums[4][4][4] = -1; nums[4][4][6] = -1; nums[4][4][7] = -1;

nums[4][5][3] = -1; nums[4][5][6] = -1; nums[4][5][7] = -1;

nums[4][6][2] = -1; nums[4][6][3] = -1; nums[4][6][6] = -1; nums[4][6][7] = -1;

nums[4][7][2] = -1; nums[4][7][6] = -1; nums[4][7][7] = -1;

nums[4][8][1] = -1; nums[4][8][2] = -1; nums[4][8][6] = -1; nums[4][8][7] = -1;

nums[4][9][1] = -1; nums[4][9][6] = -1; nums[4][9][7] = -1;

nums[4][10][0] = -1; nums[4][10][1] = -1; nums[4][10][2] = -1; nums[4][10][3] = -1; nums[4][10][4] = -1; nums[4][10][5] = -1; nums[4][10][6] = -1; nums[4][10][7] = -1; nums[4][10][8] = -1; nums[4][10][9] = -1;

nums[4][11][0] = -1; nums[4][11][1] = -1; nums[4][11][2] = -1; nums[4][11][3] = -1; nums[4][11][4] = -1; nums[4][11][5] = -1; nums[4][11][6] = -1; nums[4][11][7] = -1; nums[4][11][8] = -1; nums[4][11][9] = -1;

nums[4][12][6] = -1; nums[4][12][7] = -1;

nums[4][13][6] = -1; nums[4][13][7] = -1;

nums[4][14][6] = -1; nums[4][14][7] = -1;


// 5

nums[5][0][2] = -1; nums[5][0][3] = -1; nums[5][0][4] = -1; nums[5][0][5] = -1; nums[5][0][6] = -1; nums[5][0][7] = -1; nums[5][0][8] = -1;

nums[5][1][1] = -1;  nums[5][1][2] = -1; nums[5][1][3] = -1; nums[5][1][4] = -1; nums[5][1][5] = -1; nums[5][1][6] = -1; nums[5][1][7] = -1;

nums[5][2][1] = -1; nums[5][2][2] = -1;

nums[5][3][1] = -1; nums[5][3][2] = -1;

nums[5][4][1] = -1; nums[5][4][2] = -1;

nums[5][5][1] = -1; nums[5][5][2] = -1;

nums[5][6][1] = -1; nums[5][6][2] = -1; nums[5][6][3] = -1; nums[5][6][4] = -1; nums[5][6][5] = -1; nums[5][6][6] = -1; nums[5][6][7] = -1;

nums[5][7][1] = -1; nums[5][7][2] = -1; nums[5][7][3] = -1; nums[5][7][4] = -1; nums[5][7][5] = -1; nums[5][7][6] = -1; nums[5][7][7] = -1; nums[5][7][8] = -1;

nums[5][8][8] = -1; nums[5][8][9] = -1;

nums[5][9][8] = -1; nums[5][9][9] = -1;

nums[5][10][8] = -1; nums[5][10][9] = -1;

nums[5][11][0] = -1; nums[5][11][1] = -1; nums[5][11][8] = -1; nums[5][11][9] = -1;

nums[5][12][0] = -1; nums[5][12][1] = -1; nums[5][12][7] = -1; nums[5][12][8] = -1;

nums[5][13][1] = -1; nums[5][13][2] = -1; nums[5][13][3] = -1; nums[5][13][4] = -1; nums[5][13][5] = -1; nums[5][13][6] = -1; nums[5][13][7] = -1;

nums[5][14][2] = -1; nums[5][14][3] = -1; nums[5][14][4] = -1; nums[5][14][5] = -1; nums[5][14][6] = -1;


// 6

nums[6][0][6] = -1; nums[6][0][7] = -1;

nums[6][1][6] = -1;

nums[6][2][5] = -1; nums[6][2][6] = -1;

nums[6][3][4] = -1; nums[6][3][5] = -1;

nums[6][4][4] = -1;

nums[6][5][3] = -1; nums[6][5][4] = -1;

nums[6][6][2] = -1; nums[6][6][3] = -1; nums[6][6][4] = -1; nums[6][6][5] = -1; nums[6][6][6] = -1;

nums[6][7][2] = -1; nums[6][7][3] = -1; nums[6][7][4] = -1; nums[6][7][5] = -1; nums[6][7][6] = -1; nums[6][7][7] = -1; nums[6][7][8] = -1;

nums[6][8][1] = -1; nums[6][8][2] = -1; nums[6][8][7] = -1; nums[6][8][8] = -1;

nums[6][9][0] = -1; nums[6][9][1] = -1; nums[6][9][8] = -1; nums[6][9][9] = -1;

nums[6][10][0] = -1; nums[6][10][1] = -1; nums[6][10][8] = -1; nums[6][10][9] = -1;

nums[6][11][0] = -1; nums[6][11][1] = -1; nums[6][11][8] = -1; nums[6][11][9] = -1;

nums[6][12][0] = -1; nums[6][12][1] = -1; nums[6][12][2] = -1; nums[6][12][7] = -1; nums[6][12][8] = -1;

nums[6][13][1] = -1; nums[6][13][2] = -1; nums[6][13][3] = -1; nums[6][13][4] = -1; nums[6][13][5] = -1; nums[6][13][6] = -1; nums[6][13][7] = -1;

nums[6][14][3] = -1; nums[6][14][4] = -1; nums[6][14][5] = -1;


// 7

nums[7][0][0] = -1; nums[7][0][1] = -1; nums[7][0][2] = -1; nums[7][0][3] = -1; nums[7][0][4] = -1; nums[7][0][5] = -1; nums[7][0][6] = -1; nums[7][0][7] = -1; nums[7][0][8] = -1; nums[7][0][9] = -1;

nums[7][1][0] = -1; nums[7][1][1] = -1; nums[7][1][2] = -1; nums[7][1][6] = -1; nums[7][1][7] = -1; nums[7][1][8] = -1; nums[7][1][9] = -1;

nums[7][2][0] = -1; nums[7][2][1] = -1; nums[7][2][7] = -1; nums[7][2][8] = -1;

nums[7][3][7] = -1;

nums[7][4][7] = -1;

nums[7][5][6] = -1;

nums[7][6][5] = -1; nums[7][6][6] = -1;

nums[7][7][5] = -1;

nums[7][8][5] = -1;

nums[7][9][4] = -1; nums[7][9][5] = -1;

nums[7][10][4] = -1;

nums[7][11][3] = -1; nums[7][11][4] = -1;

nums[7][12][3] = -1;

nums[7][13][2] = -1; nums[7][13][3] = -1;

nums[7][14][2] = -1;


// 8

nums[8][0][4] = -1; nums[8][0][5] = -1;

nums[8][1][2] = -1; nums[8][1][3] = -1; nums[8][1][4] = -1; nums[8][1][5] = -1; nums[8][1][6] = -1; nums[8][1][7] = -1;

nums[8][2][1] = -1; nums[8][2][2] = -1; nums[8][2][7] = -1; nums[8][2][8] = -1;

nums[8][3][1] = -1; nums[8][3][8] = -1;

nums[8][4][0] = -1; nums[8][4][1] = -1; nums[8][4][8] = -1;

nums[8][5][1] = -1; nums[8][5][8] = -1;

nums[8][6][2] = -1; nums[8][6][3] = -1; nums[8][6][4] = -1; nums[8][6][5] = -1; nums[8][6][6] = -1; nums[8][6][7] = -1;

nums[8][7][2] = -1; nums[8][7][3] = -1; nums[8][7][4] = -1; nums[8][7][5] = -1; nums[8][7][6] = -1; nums[8][7][7] = -1;

nums[8][8][1] = -1; nums[8][8][2] = -1; nums[8][8][7] = -1; nums[8][8][8] = -1;

nums[8][9][0] = -1; nums[8][9][1] = -1; nums[8][9][8] = -1; nums[8][9][9] = -1;

nums[8][10][0] = -1; nums[8][10][1] = -1; nums[8][10][8] = -1; nums[8][10][9] = -1;

nums[8][11][0] = -1; nums[8][11][1] = -1; nums[8][11][8] = -1; nums[8][11][9] = -1;

nums[8][12][1] = -1; nums[8][12][2] = -1; nums[8][12][7] = -1; nums[8][12][8] = -1;

nums[8][13][1] = -1; nums[8][13][2] = -1; nums[8][13][3] = -1; nums[8][13][6] = -1; nums[8][13][7] = -1;

nums[8][14][3] = -1; nums[8][14][4] = -1; nums[8][14][5] = -1; nums[8][14][6] = -1;


// 9

nums[9][0][3] = -1; nums[9][0][4] = -1; nums[9][0][5] = -1;

nums[9][1][1] = -1; nums[9][1][2] = -1; nums[9][1][3] = -1; nums[9][1][4] = -1; nums[9][1][5] = -1; nums[9][1][6] = -1; nums[9][1][7] = -1;

nums[9][2][0] = -1; nums[9][2][1] = -1; nums[9][2][7] = -1; nums[9][2][8] = -1;

nums[9][3][0] = -1; nums[9][3][1] = -1; nums[9][3][7] = -1; nums[9][3][8] = -1;

nums[9][4][0] = -1; nums[9][4][1] = -1; nums[9][4][7] = -1; nums[9][4][8] = -1;

nums[9][5][1] = -1; nums[9][5][2] = -1; nums[9][5][8] = -1; nums[9][5][9] = -1;

nums[9][6][1] = -1; nums[9][6][2] = -1; nums[9][6][3] = -1; nums[9][6][8] = -1; nums[9][6][9] = -1;

nums[9][7][3] = -1; nums[9][7][4] = -1; nums[9][7][5] = -1; nums[9][7][6] = -1; nums[9][7][7] = -1; nums[9][7][8] = -1;

nums[9][8][5] = -1; nums[9][8][6] = -1; nums[9][8][7] = -1;

nums[9][9][6] = -1; nums[9][9][7] = -1;

nums[9][10][6] = -1;

nums[9][11][6] = -1; nums[9][11][7] = -1;

nums[9][12][5] = -1;

nums[9][13][4] = -1; nums[9][13][5] = -1;


// 자음 및 모음 중략...


이런식으로 3차원 배열에 이미지 템플릿(자음, 모음, 숫자)에 해당하는 픽셀값을 미리 설정해 두고 추출된 이미지와 1:1 비교해서 일치하는 값만큼 숫자를 증가시켜 그 숫자의 비율을 정확도로 칭하고 그 정확도가 가장 높은 숫자를 인식된 숫자라고 판별합니다.


다음은 실행 결과입니다.





프로그램 코드가 너무 긴 관계로 일부 코드를 생략했습니다. 무조건 복사 붙여넣기 한다고 실행 되지 않을것입니다. 프로그램 원리나 소스코드에 대해 궁금하신분은 댓글 또는 방명록에 남겨주시면 상세하게 답변해드리겠습니다. 

공감♡ 버튼을 눌러주시면 더욱 유용하고 좋은 포스팅으로 찾아뵙겠습니다.









안드로이드 프로그래밍 중 텍스트를 출력시키고 싶다면 TextView를 사용해보세요.

TextView는 View클래스를 상속받아 만들어진 클래스입니다.


※ 참고

만약 유저가 수정할 수 있는 텍스트란을 원하신다면 EditText를 사용해보세요.


XML Layout에서 기본적인 사용법입니다.


<TextView

android:layout_height="wrap_content"

android:layout_width="wrap_content"

android:id="@+id/text_view_id"

android:text="Hello World!" />


layout_height와 width는 텍스트뷰의 크기를 지정해 줍니다. wrap_content는 텍스트 뷰 내용에 맞게 맞춰진다는 뜻입니다.

id는 현재 텍스트뷰에 이름을 붙여준다고 생각합시다.(다른 누군가가 부를때 이름이 없으면 부를수가 없겠죠?)

text는 텍스트뷰에 표시 될 텍스트(문구)를 적습니다.


실행한다면 화면 좌측 상단에 Hello World!라고 작게 찍히는 모습을 확인 할 수 있습니다.


MainActivity로 가서 onCreate 함수 안에 다음과 같이 작성해봅시다.


TextView textView = (TextView) findViewById(R.id.text_view_id);

textView.setText("World Hello!");


TextView의 객체를 선언해주고 값을 findViewById 함수를 통해 XML에서 설정해주는 id(텍스트뷰의 이름)로 정의합니다.

그리고 setText함수를 통해 출력 될 텍스트를 설정하거나 바꿀 수 있습니다.


다음은 XML TextView attributes(속성)입니다.


autoText : 스펠링 검사를 자동으로 수행합니다.

cursorVisible : 텍스트 뷰의 커서를 보일지 설정합니다.

digits : 텍스트뷰에 숫자만 입력하도록 설정합니다.

editable : 텍스트뷰의 텍스트를 수정할 수 있는지 설정합니다.

ems : 텍스트뷰의 기본 길이를 설정합니다.

gravity : 텍스뷰의 텍스트가 나타날 위치를 지정합니다.

height : 텍스트뷰의 높이를 설정합니다.

hint : 텍스트 뷰가 비어있을 때 나타날 문자를 설정합니다.

inputType : 텍스트 뷰에 입력 될 텍스트의 입력 타입을 설정합니다.

- numeric : 숫자만 입력합니다.

- password : 입력된 숫자를 *로 표기합니다.

- phoneNumber : 휴대폰 번호 입력.

lines : 텍스트 뷰의 줄(라인)을 설정합니다.

maxEms : ems의 최대 길이를 설정합니다.

maxHeight : 텍스트 뷰의 최대 높이를 설정합니다.

maxLength : 텍스트 뷰에 입력 될 텍스트의 최대 길이를 설정합니다.

maxLines : 텍스트 뷰의 최대 줄 수를 설정합니다.

maxWidth : 텍스트 뷰의 최대 너비를 설정합니다.

※ min은 모두 최소값을 설정하는 속성입니다.

textColor : 텍스트 뷰에 입력 될 텍스트의 색깔을 설정합니다.

textSize : 텍스트 뷰에 입력 될 텍스트의 크기를 설정합니다.


다음은 Acitivity(Java class)에서 사용되는 TextView Class Public Methods(함수)입니다. 

getHint() : 텍스트 뷰에 설정된 hint 내용을 반환합니다.

setHint("hint") : 텍스트 뷰에 "hint"를 설정합니다.

getInputType() : 텍스트 뷰에 설정된 InputType을 반환합니다.

setInputType(int) : 텍스트 뷰의 입력 타입을 설정합니다. (type은 EditorInfo.inputType에 정의되어 있습니다.)

getLineCount() : 현재 입력된 텍스트 뷰의 라인 수를 반환합니다.

getMaxEms() : 텍스트 뷰의 최대 ems를 반환합니다.

setMaxEms(int) : 텍스트 뷰의 최대 ems를 설정합니다.

getMaxHeight() : 텍스트 뷰의 최대 높이를 반환합니다.

setMaxHeight(int) : 텍스트 뷰의 최대 높이를 설정합니다.

getMaxLines() : 텍스트 뷰의 최대 라인 수를 반환합니다.

setMaxLines(int) : 텍스트 뷰의 최대 라인 수를 설정합니다.

getMaxWidth() : 텍스트 뷰의 최대 너비를 반환합니다.

setMaxWidth(int) : 텍스트 뷰의 최대 너비를 설정합니다.

getText() : 텍스트 뷰에 입력된 텍스트를 반환합니다.

setText("text") : 텍스트 뷰에 "text"를 출력하도록 설정합니다.

append("text") : 텍스트 뷰에 "text"를 덧붙여 씁니다. (기존에 존재하던 텍스트 바로 뒤에 붙여서 입력됩니다.)

getTextSize() : 텍스트 뷰의 텍스트 크기를 반환합니다.

setTextSize(int) : 텍스트 뷰의 텍스트 크기를 설정합니다.


TextView에 대해 더 궁금한 사항이 있으시면 여기를 확인하세요.


2018/08/20 - [Language/Android] - 안드로이드 EditText 사용법 정리

2018/08/20 - [Language/Android] - [Android] 안드로이드 키보드 입력 창 올리기/ 내리기



정보가 유익하셨다면 아래 공감버튼 눌러주시면 감사하겠습니다.

질문사항은 댓글로 달아주시면 성의껏 답변해드리겠습니다.



to Top