개인 프로젝트/[2021] 코드악보 공유APP

6. 악보 검색 / 등록 페이지 제작 (4) - 검색기능 구현 & 악보 뷰어 제작

2021. 3. 13. 00:26
반응형

악보의 세부 내용을 입력받기 전에 간단하게 검색부터 후딱 구현해보았다.

우선 검색 버튼의 이벤트를 읽어올 리스너를 달아주고,

검색 이벤트가 발생했을 때 검색을 수행할 코드를 작성하였다.

 

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.menu_tab_sheet, menu);

        mSearch = menu.findItem(R.id.tab_sheet_top_search);

        SearchView searchView = (SearchView)mSearch.getActionView();
        searchView.setQueryHint("검색할 곡 이름을 입력하세요.");
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                try {
                    SelectData selectData = new SelectData();
                    selectData.execute("http://" + IP_ADDRESS + "/" + SELECT_PHP, query);
                    searchView.clearFocus();
                    return true;
                } catch (Exception e) {
                    Log.d("Test", e.getMessage());
                    return false;
                }
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });
    }

상단바의 검색 메뉴 아이템을 가져온 후, 해당 아이템에 달려있는 액션뷰를 가져온다.

그리고 액션뷰에 '검색'버튼을 눌렀을 때의 이벤트 함수를 작성한다.

간단하게 파라미터로 넘어온 검색어(qeury)를 조건으로 해서 서버에서 데이터를 조회하면 된다.

 

조회를 위해서 AsyncTask를 상속받는 SelectData 클래스를 따로 정의했다.

 

class SelectData extends AsyncTask<String, Void, String> {
            ProgressDialog progressDialog;

            @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog = ProgressDialog.show(context, "plz wait", null, true, true);
        }

        @Override
        protected String doInBackground(String... params) {

            // execute 메소드 실행시 들어간 인자들이 params에 할당된다.
            String serverURL = (String)params[0];
            String query     = (String)params[1];

            String postParameters = "query=" + query;

            try {
                URL url = new URL(serverURL);
                HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();

                httpURLConnection.setReadTimeout(5000);
                httpURLConnection.setConnectTimeout(5000);
                httpURLConnection.setRequestMethod("POST");
                httpURLConnection.setDoInput(true);
                httpURLConnection.connect();

                // 웹 서버에 요청 전송
                OutputStream outputStream = httpURLConnection.getOutputStream();
                outputStream.write(postParameters.getBytes("UTF-8"));
                outputStream.flush();
                outputStream.close();

                // 웹 서버로부터 응답 수신
                InputStream inputStream;
                int responseStatusCode = httpURLConnection.getResponseCode();

                if (responseStatusCode == HttpURLConnection.HTTP_OK) {
                    inputStream = httpURLConnection.getInputStream();
                }
                else {
                    inputStream = httpURLConnection.getErrorStream();
                }

                InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

                StringBuilder stringBuilder = new StringBuilder();
                String line = null;

                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line);
                    Log.d("Test", line);
                }

                bufferedReader.close();

                return stringBuilder.toString().trim();
            } catch (Exception e) {
                Log.d("Test", "DoInBackground : " + e.getMessage());
                return null;
            }
        }

        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);

            progressDialog.dismiss();

            if (s == null) {
                AlertDialog.Builder builder = new AlertDialog.Builder(context);
                builder.setMessage("조회 중 에러가 발생했습니다.");
                builder.create().show();
            }

            else {
                try {
                    JSONObject jsonObject = new JSONObject(s);
                    JSONArray  jsonArray  = jsonObject.getJSONArray(TAG_JSON);

                    adapter = new ItemSheetAdapter();

                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject item = jsonArray.getJSONObject(i);

                        String sheet_id  = item.getString(TAG_SHEET_ID);
                        String song_name = item.getString(TAG_SONG_NAME);
                        String singer    = item.getString(TAG_SINGER);

                        adapter.addItem(new ItemSheetList(sheet_id, song_name, singer));
                    }

                    adapter.setOnItemClickListener(new OnSheetItemClickListener() {
                        @Override
                        public void onItemClick(ItemSheetAdapter.ViewHolder viewHolder, View view, int position) {
                            ItemSheetList item = adapter.getItem(position);
                            Intent viewSheetIntent = new Intent(context, SheetViewerActivity.class);

                            viewSheetIntent.putExtra("sheet_id", item.getSheet_id());
                            viewSheetIntent.putExtra("song_name",item.getSong_name());
                            viewSheetIntent.putExtra("singer",item.getSinger());

                            startActivity(viewSheetIntent);
                        }
                    });

                    recyclerView.setAdapter(adapter);
                }
                catch (Exception e) {
                    Log.d("Test","onPostExcuse : " + e.getMessage());
                }
            }
        }
    }

코드가 많이 길지만 함수별로 나눠서 보면 간단하다.

onPreExecute() -> 프로그래스 다이어로그르 띄우기

onExecute() -> 검색어로 쿼리 실행시키기

onPostExecute() -> 프로그래스 다이어로그 없애고, 쿼리 조회 결과를 JSON으로 가져와서

각각의 JSON객체를 아이템으로 추가해주는 동시에 각각의 아이템에 onItemClick 리스너 달아주기

 

이제 리사이클러 뷰에 있는 아이템(악보)을 클릭했을 때 해당 아이템(악보)의 내용을 보는 창을 띄울 것이다.

이를 위해서 클릭 리스너를 달아주었다.

 

이 과정은 Do It 안드로이드 프로그래밍의 설명을 참고해서 만들었다.

 

package com.everdu.chordshare;

import android.view.View;

public interface OnSheetItemClickListener {
    public void onItemClick(ItemSheetAdapter.ViewHolder viewHolder, View view, int position);
}

 

인터페이스를 따로 만들어주고나서

 

public class ItemSheetAdapter extends RecyclerView.Adapter<ItemSheetAdapter.ViewHolder>
                            implements OnSheetItemClickListener {

    ArrayList<ItemSheetList> items = new ArrayList<ItemSheetList>();
    OnSheetItemClickListener listener;

    @NonNull
    @Override
    public ItemSheetAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
        View itemView = inflater.inflate(R.layout.item_sheet_list, parent, false);

        return new ViewHolder(itemView, this);
    }

    @Override
    public void onBindViewHolder(@NonNull ItemSheetAdapter.ViewHolder holder, int position) {
        ItemSheetList item = items.get(position);
        holder.setItem(item);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    public void addItem(ItemSheetList item) {
        items.add(item);
    }

    public void setItems(ArrayList<ItemSheetList> items) {
        this.items = items;
    }

    public ItemSheetList getItem(int position) {
        return items.get(position);
    }

    public void setOnItemClickListener(OnSheetItemClickListener listener) {
        this.listener = listener;
    }

    @Override
    public void onItemClick(ViewHolder viewHolder, View view, int position) {
        if (listener != null) {
            listener.onItemClick(viewHolder, view, position);
        }
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView song_name;
        TextView singer;

        public ViewHolder(@NonNull View itemView, final OnSheetItemClickListener listener) {
            super(itemView);

            song_name = itemView.findViewById(R.id.txv_song_name);
            singer    = itemView.findViewById(R.id.txv_singer);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = getAdapterPosition();
                    if (listener != null) {
                        listener.onItemClick(ViewHolder.this, v, position);
                    }
                }
            });
        }

        public void setItem(ItemSheetList item) {
            song_name.setText(item.song_name);
            singer.setText(item.singer);
        }
    }
}

 

어댑터의 소스코드를 다음처럼 수정했다.

리사이클러 뷰의 뷰홀더에 리스너를 달아주도록 한 코드이다.

 

마지막으로 php파일의 내용을 조금 수정해주면 끝난다.

<?php 
    error_reporting(E_ALL); 
    ini_set('display_errors',1); 

    include('DbConn.php');

	try {
		$query = $_POST['query'];

		#echo $query;

		$select_qry = 'select * '
					 .'  from sheet_list ';

		if (!empty($query)) {
			$select_qry .= (" where song_name like '%".$query."%'");
		} 
		
		#test#
		#echo $select_qry;

		$stmt = $con->prepare($select_qry);

		if($stmt->execute())
		{
			if ($stmt->rowCount() > 0)
			{
				$data = array(); 

				while($row=$stmt->fetch(PDO::FETCH_ASSOC))
				{
					extract($row);
    
					array_push($data, 
						array('sheet_id'=>$row["sheet_id"],
							'song_name'=>$row["song_name"],
							'singer'=>$row["singer"]
						)
					);
				}

				header('Content-Type: application/json; charset=utf8');
				$json = json_encode(array("qry_result"=>$data), JSON_PRETTY_PRINT+JSON_UNESCAPED_UNICODE);
				echo $json;
				#echo "testing~";
			}
		}
		
		else {
			echo iconv("euckr", "utf-8", "쿼리 에러");	
		}
	}

	catch(PDOException $e) {
		die("Database error: " . $e->getMessage()); 
	}
?>

 

처음에 습관적으로 자바에서 쓰듯 +로 문자열을 연결했다가 오류가 나와서 당황했었다

언어별 특징도 좋지만 어느 정도 공통적인 부분은 통일을 하면 좋지 않을까 하는 생각이 든다ㅋㅋ

 

현재 만들어둔 악보 리스트는 다음과 같다.

 

 

이제 여기에서 '내 손' 을 검색하면 다음과 같이 결과가 나온다.

 

하지만 '내손' 이라고 검색한다면 저 둘은 검색되지 않는다.

mysql의 replace 함수를 사용해서 문자열 내 공백을 모두 지운 다음 검색하면 해결이 될 것 같지만

테스트를 해보니 좀 불완전해서 좀 더 기능을 다듬는 것을 고민해보고 적용해야 할 것 같다.

 

그리고 현재는 검색 결과가 없을 때 화면이 바뀌지 않는 문제가 있다.

아무 아이템을 추가하지 않은 어댑터를 세팅해서 당연히 없는 결과가 나올 것이라고 생각했는데 의외였다..

 

이 부분은 나중에 해결해야할 것 같다.

 

다음으로 간단하게 악보 뷰어 액티비티를 만들었다.

악보의 식별은 sheet_id로 하기 때문에, 해당 정보를 인텐트로 넘겨주도록 하였다.

 

package com.everdu.chordshare;

import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;

import android.app.Notification;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;

public class SheetViewerActivity extends AppCompatActivity {

    ActionBar actionBar;

    String sheet_id;
    String song_name;
    String singer;

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

        Intent intent = getIntent();

        sheet_id  = intent.getStringExtra("sheet_id");
        song_name = intent.getStringExtra("song_name");
        singer    = intent.getStringExtra("singer");

        actionBar = getSupportActionBar();
        actionBar.setTitle(song_name);

    }
}

 

간단하게 코드를 작성해서 테스트 해보았다.

리스트를 클릭하면 다음과 같이 나온다.

 

 

간단하게 곡 이름을 인텐트로 받아와 타이틀에 넣어주도록 하였다.

다음으로는 코드 악보를 그려넣기 위한 코드 정보를 저장할 테이블 레이아웃을 설계하고,

테이블에 데이터를 저장하는 액티비티를 완성할 것이다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'개인 프로젝트 > [2021] 코드악보 공유APP' 카테고리의 다른 글

8. 플러터로 프로젝트 이전 (1) - 바텀네비게이션, 검색UI, http통신  (2) 2021.07.06
7. 악보 검색 / 등록 페이지 제작 (5) - 악보 추가 기능 만들기(1)  (2) 2021.03.18
5. 악보 검색 / 등록 페이지 제작 (3) - DB연동하여 데이터 추가/조회하기  (0) 2021.03.11
4. 악보 검색 / 등록 페이지 제작 (2) - 프래그먼트에 리사이클러 뷰 추가  (0) 2021.03.11
3. 악보 검색 / 등록 페이지 제작 (1) - 바텀 네비게이션 추가  (0) 2021.03.10
'개인 프로젝트/[2021] 코드악보 공유APP' 카테고리의 다른 글
  • 8. 플러터로 프로젝트 이전 (1) - 바텀네비게이션, 검색UI, http통신
  • 7. 악보 검색 / 등록 페이지 제작 (5) - 악보 추가 기능 만들기(1)
  • 5. 악보 검색 / 등록 페이지 제작 (3) - DB연동하여 데이터 추가/조회하기
  • 4. 악보 검색 / 등록 페이지 제작 (2) - 프래그먼트에 리사이클러 뷰 추가
에버듀
에버듀
개발은 좋은데 뭘로 개발할까
에버듀
Blog. 에버듀
에버듀
전체
오늘
어제
  • 분류 전체보기 (587)
    • 개인 프로젝트 (43)
      • [2020] 카카오톡 봇 (9)
      • [2021] 코드악보 공유APP (22)
      • [2022] 유튜브 뮤직 클론코딩 (9)
      • 간단한 프로젝트 (3)
    • 팀 프로젝트 (22)
      • [2020] 인공지능 숫자야구 (4)
      • [2022] OSAM 온라인 해커톤 (10)
      • [2024] GDSC 프로젝트 트랙 (6)
      • [2025] 큰소리 웹 페이지 (2)
    • 알고리즘 (PS) (107)
      • BOJ (101)
      • Programmers (5)
      • 알고리즘 이모저모 (1)
    • CS (312)
      • 자료구조 (19)
      • 어셈블리 (41)
      • 멀티미디어응용수학 (7)
      • 컴퓨터 구조 (29)
      • 알고리즘 분석 (4)
      • 컴퓨터 네트워크 (38)
      • 프로그래밍언어론 (15)
      • HCI 윈도우즈프로그래밍 (26)
      • 기초데이터베이스 (29)
      • 운영체제 (23)
      • 오토마타 (24)
      • 문제해결기법 (11)
      • 블록체인 (22)
      • 소프트웨어공학 (12)
      • 기계학습심화 (12)
    • 자기계발 (35)
      • 동아리 (7)
      • 자격증 (2)
      • 코딩테스트, 대회 (8)
      • 생각 정리 (17)
      • 머니 스터디 (1)
    • WEB(BE) (5)
      • express.js (1)
      • flask (0)
      • Spring & Spring Boot (4)
    • WEB(FE) (2)
      • html, css, js (1)
      • React.js (1)
    • Tool & Language (6)
      • Edit Plus (1)
      • Git (1)
      • Python3 (2)
      • Java (2)
    • Infra (12)
      • AWS (1)
      • Oracle Cloud (8)
      • Firebase (2)
      • Network (1)
    • Android (18)
      • Java (6)
      • Flutter (12)
    • Window (2)
      • Visual Studio 없이 WPF (1)
      • MFC (1)
    • 독서 (14)
      • Inside Javascript (7)
      • Database Internals (6)
      • 한 글 후기 (1)
    • 인턴 (8)
      • 델파이 (7)
      • Oracle (1)

인기 글

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.1.4
에버듀
6. 악보 검색 / 등록 페이지 제작 (4) - 검색기능 구현 & 악보 뷰어 제작
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.