본문 바로가기

안드로이드 스튜디오

[안드로이드 스튜디오]유튜브API 이용하여 앱에 검색결과 출력하기

https://rudtjr0906.tistory.com/126

유튜브 API 사용 방법 및 키 값 사용 방법을 먼저 참고하고 포스팅을 보기 바란다.

 

[안드로이드 스튜디오]유튜브 API를 postman으로 활용하여 불러오기

먼저 여기로 들어가서 YouTube Data API 개요를 살펴본다.https://developers.google.com/youtube/v3/getting-started?hl=ko YouTube Data API 개요  |  Google for Developers이 페이지는 Cloud Translation API를 통해 번역되었습니다.

rudtjr0906.tistory.com


기능설계

  • 검색어를 입력하고 검색(돋보기) 버튼을 누르면 유튜브에서 검색 결과를 뷰에 표시한다.
  • 썸네일을 누르면 썸네일의 큰 이미지가 새로운 액티비티에 표시된다.
  • 출력된 결과를 클릭하면 웹브라우저가 켜지며 유튜브 영상이 재생이 된다.
  • 출력 결과를 스크롤하여 리스트의 마지막에 도달 할 경우, 검색 결과가 더 존재하면 다음 페이지로 새로고침한다.

 

먼저 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에서 선택된 동영상의 썸네일을 전체 화면에 표시하는 기능을 구현했다. 여기까지가 앱 개발의 주요 과정이다.

 


실행결과

축구 검색 후 이미지 클릭시 확대 되는 모습

 

축구 검색 후 스크롤로 내려보면 잘 내려가고 카드 뷰 클릭 시 유튜브 웹 브라우저로 넘어가면서 영상 재생