악보의 세부 내용을 입력받기 전에 간단하게 검색부터 후딱 구현해보았다.
우선 검색 버튼의 이벤트를 읽어올 리스너를 달아주고,
검색 이벤트가 발생했을 때 검색을 수행할 코드를 작성하였다.
@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 |