https://rudtjr0906.tistory.com/126
유튜브 API 사용 방법 및 키 값 사용 방법을 먼저 참고하고 포스팅을 보기 바란다.
기능설계
- 검색어를 입력하고 검색(돋보기) 버튼을 누르면 유튜브에서 검색 결과를 뷰에 표시한다.
- 썸네일을 누르면 썸네일의 큰 이미지가 새로운 액티비티에 표시된다.
- 출력된 결과를 클릭하면 웹브라우저가 켜지며 유튜브 영상이 재생이 된다.
- 출력 결과를 스크롤하여 리스트의 마지막에 도달 할 경우, 검색 결과가 더 존재하면 다음 페이지로 새로고침한다.
먼저 Gradle에서 필요한 라이브러리 설치
implementation ("com.github.bumptech.glide:glide:4.16.0")
implementation("com.android.volley:volley:1.2.1")
메인 화면 레이아웃 (activity_main.xml)
메인 화면에서는 검색 기능과 동영상 목록을 표시한다. 이를 위해 RecyclerView, EditText, ImageView, ProgressBar를 사용했다.
카드뷰 레이아웃(video_row.xml, activity_photo.xml)
검색어를 입력시 출력되는 카드뷰들이다.
리사이클러뷰에 나타나야할 카드뷰 XML인데,이미지를 클릭시 이미지가 확대되고 카드뷰 클릭시 유튜브 웹 브라우저가 켜지며 영상이 재생이 되도록 만들 것이다.
클래스 구현
이제 레이아웃 파일을 설정했으니, 각 화면의 기능을 구현하는 Java 코드를 작성해보자.
MainActivity.java, PhotoActivity.java, Video.java, Config.java, VideoAdapter.java 파일을 다룬다.
MainActivity.java
MainActivity는 메인 화면의 기능을 구현한다. 검색 기능과 동영상 목록 표시를 담당한다
package com.kks.youtube;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.material.snackbar.Snackbar;
import com.kks.youtube.adapter.VideoAdapter;
import com.kks.youtube.config.Config;
import com.kks.youtube.model.Video;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
EditText editSearch;
ImageView imgSearch;
ProgressBar progressBar;
RecyclerView recyclerView;
String nextPageToken;
String keyword;
ArrayList<Video> videoArrayList = new ArrayList<>();
VideoAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editSearch = findViewById(R.id.editSearch);
imgSearch = findViewById(R.id.imgSearch);
progressBar = findViewById(R.id.progressBar);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
// 스크롤 처리를 위한 코드
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// 맨 마지막 데이터가 화면에 보이게 되면,
// 네트워크 통해서 데이터를 추가로 가져오도록 한다.
int lastPosition = ((LinearLayoutManager)recyclerView.getLayoutManager())
.findLastVisibleItemPosition();
int totalCount = recyclerView.getAdapter().getItemCount();
// 인덱스가 데이터 개수많큼 왔는지 확인
if( lastPosition + 1 == totalCount){
// 마지막 데이터가 화면에 나타났는가?
// 네트워크로부터 데이터를 추가로 받아온다.
addNetworkData(); // 네트워크로부터 데이터를 추가로 받아오는 함수 만든다.
}
}
});
progressBar.setVisibility(View.GONE);
imgSearch.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
keyword = editSearch.getText().toString().trim();
if(keyword.isEmpty()){
Snackbar.make(imgSearch,
"검색어를 입력하세요.",
Snackbar.LENGTH_SHORT).show();
return;
}
// 네트워크 API 를 호출한다.
getNetworkData();
}
});
}
// 추가로 20개씩을 더 호출할때 사용하는 함수.
private void addNetworkData() {
progressBar.setVisibility(View.VISIBLE);
// 유튜브 API 를 호출해서, 데이터를 받아온다.
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
String url = "https://www.googleapis.com/youtube/v3/search?key=" + Config.YOUTUBE_KEY +
"&part=snippet&maxResults=20&order=date&type=video&q=" + keyword + "&pageToken=" + nextPageToken;
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.GET,
url,
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i("YOUTUBE MAIN", response.toString());
progressBar.setVisibility(View.GONE);
try {
nextPageToken = response.getString("nextPageToken");
JSONArray items = response.getJSONArray("items");
for(int i = 0; i < items.length(); i++){
JSONObject data = items.getJSONObject(i);
JSONObject id = data.getJSONObject("id");
String videoId = id.getString("videoId");
JSONObject snippet = data.getJSONObject("snippet");
String title = snippet.getString("title");
String description = snippet.getString("description");
JSONObject thumbnails = snippet.getJSONObject("thumbnails");
JSONObject medium = thumbnails.getJSONObject("medium");
String thumbUrl = medium.getString("url");
JSONObject high = thumbnails.getJSONObject("high");
String url = high.getString("url");
Video video = new Video(videoId, title, description, thumbUrl, url);
videoArrayList.add(video);
}
adapter.notifyDataSetChanged();
} catch (JSONException e) {
// todo 유저한테 알려주고, 로그도 찍고 리턴.
return;
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// todo 유저한테 알려주고, 로그도 찍고 리턴.
Log.i("YOUTUBE MAIN", error.toString());
progressBar.setVisibility(View.GONE);
}
}
);
queue.add(request);
}
// 처음 20개 데이터를 가져오는 함수
private void getNetworkData() {
progressBar.setVisibility(View.VISIBLE);
videoArrayList.clear();
// 유튜브 API 를 호출해서, 데이터를 받아온다.
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
String url = "https://www.googleapis.com/youtube/v3/search?key=" + Config.YOUTUBE_KEY +
"&part=snippet&maxResults=20&order=date&type=video&q=" + keyword;
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.GET,
url,
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.i("YOUTUBE MAIN", response.toString());
progressBar.setVisibility(View.GONE);
try {
nextPageToken = response.getString("nextPageToken");
JSONArray items = response.getJSONArray("items");
for(int i = 0; i < items.length(); i++){
JSONObject data = items.getJSONObject(i);
JSONObject id = data.getJSONObject("id");
String videoId = id.getString("videoId");
JSONObject snippet = data.getJSONObject("snippet");
String title = snippet.getString("title");
String description = snippet.getString("description");
JSONObject thumbnails = snippet.getJSONObject("thumbnails");
JSONObject medium = thumbnails.getJSONObject("medium");
String thumbUrl = medium.getString("url");
JSONObject high = thumbnails.getJSONObject("high");
String url = high.getString("url");
Video video = new Video(videoId, title, description, thumbUrl, url);
videoArrayList.add(video);
}
adapter = new VideoAdapter(MainActivity.this, videoArrayList);
recyclerView.setAdapter(adapter);
} catch (JSONException e) {
// todo 유저한테 알려주고, 로그도 찍고 리턴.
return;
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// todo 유저한테 알려주고, 로그도 찍고 리턴.
Log.i("YOUTUBE MAIN", error.toString());
progressBar.setVisibility(View.GONE);
}
}
);
queue.add(request);
}
}
PhotoActivity.java
PhotoActivity는 사진을 전체 화면에 표시하는 기능을 구현한다.
package com.kks.youtube;
import static com.kks.youtube.R.*;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.kks.youtube.model.Video;
public class PhotoActivity extends AppCompatActivity {
ImageView imgPhoto;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
Video video = (Video) getIntent().getSerializableExtra("video");
imgPhoto = findViewById(R.id.imgPhoto);
Glide.with(PhotoActivity.this).load(video.url).into(imgPhoto);
}
}
Video.java
Video 클래스는 동영상 데이터를 모델링한다.
package com.kks.youtube.model;
import java.io.Serializable;
public class Video implements Serializable {
public String videoId;
public String title;
public String description;
public String thumbUrl;
public String url;
public Video(){
}
public Video(String videoId, String title, String description, String thumbUrl, String url) {
this.videoId = videoId;
this.title = title;
this.description = description;
this.thumbUrl = thumbUrl;
this.url = url;
}
}
Config.java
Config 클래스는 키 값 을 관리하거나, 앱의 상수를 관리한다.
public class Config {
// 변하지 않게 하기위해 final을 쓰고 static을 써서 데이터 영역(힙보다 높은 영역)에 넣고 public을 써서 어디서든
// 사용할 수 있도록 한다.
public static final String YOUTUBE_KEY = "자신의 키값을 입력";
}
VideoAdapter.java
VideoAdapter 클래스는 RecyclerView의 어댑터로서 동영상 목록을 관리한다.
package com.kks.youtube.adapter;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.kks.youtube.PhotoActivity;
import com.kks.youtube.R;
import com.kks.youtube.model.Video;
import java.util.ArrayList;
public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.ViewHolder>{
Context context;
ArrayList<Video> videoArrayList;
public VideoAdapter(Context context, ArrayList<Video> videoArrayList) {
this.context = context;
this.videoArrayList = videoArrayList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.video_row, parent, false);
return new VideoAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Video video = videoArrayList.get(position);
holder.txtTitle.setText( video.title );
holder.txtDescription.setText( video.description );
Glide.with(context).load( video.thumbUrl ).into( holder.imgThumb );
}
@Override
public int getItemCount() {
return videoArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView txtTitle;
TextView txtDescription;
ImageView imgThumb;
CardView cardView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txtTitle = itemView.findViewById(R.id.txtTitle);
txtDescription = itemView.findViewById(R.id.txtDescription);
imgThumb = itemView.findViewById(R.id.imgThumb);
cardView = itemView.findViewById(R.id.cardView);
imgThumb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, PhotoActivity.class);
int index = getAdapterPosition();
Video video = videoArrayList.get(index);
intent.putExtra("video", video);
context.startActivity(intent);
}
});
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = getAdapterPosition();
Video video = videoArrayList.get(index);
String url = "https://www.youtube.com/watch?v=" + video.videoId;
openWebPage(url);
}
});
}
// 웹브라우저 액티비티를 실행시키는 함수
void openWebPage(String url){
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
context.startActivity(intent);
}
}
}
이제 MainActivity.java에서 검색 기능을 통해 동영상 목록을 필터링하고, PhotoActivity.java에서 선택된 동영상의 썸네일을 전체 화면에 표시하는 기능을 구현했다. 여기까지가 앱 개발의 주요 과정이다.
실행결과
축구 검색 후 이미지 클릭시 확대 되는 모습
축구 검색 후 스크롤로 내려보면 잘 내려가고 카드 뷰 클릭 시 유튜브 웹 브라우저로 넘어가면서 영상 재생
'안드로이드 스튜디오' 카테고리의 다른 글
[안드로이드 스튜디오]Retrofit2 라이브러리를 이용하여 메모 API 서버와 통신하기(1)화면 구성 (4) | 2024.06.16 |
---|---|
[안드로이드 스튜디오]Retrofit2 라이브러리 사용을 위한 설정 방법(Gson 컨버터,OkHttp 로깅 인터셉터) (0) | 2024.06.16 |
[안드로이드 스튜디오]유튜브 API를 postman으로 활용하여 불러오기 (2) | 2024.06.12 |
[안드로이드 스튜디오]안드로이드 인텐트를 활용한 다양한 기능 구현하기 (2) | 2024.06.12 |
[안드로이드 스튜디오]안드로이드 앱에서 Glide를 사용한 효율적인 이미지 로딩 및 디스플레이 방법 (0) | 2024.06.12 |