MainActivity
package com.example.youtube;
import android.app.DownloadManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
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.example.youtube.adapter.PostingAdapter;
import com.example.youtube.model.Posting;
import com.google.android.material.snackbar.Snackbar;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
// 멤버변수
Button btn;
EditText editText;
ProgressBar progressBar;
RecyclerView recyclerView;
ArrayList<Posting> postingArrayList = new ArrayList<>();
PostingAdapter adapter;
String text;
String nextPageToken;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
editText = findViewById(R.id.editText);
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 lastpositon=((LinearLayoutManager)recyclerView.getLayoutManager()).findLastCompletelyVisibleItemPosition();
int totalCount = recyclerView.getAdapter().getItemCount();
if(lastpositon+1 == totalCount){
// 네트워크로부터 데이터를 추가로 받아온다.
addNetworkData();
}
}
});
progressBar.setVisibility(View.GONE);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
text = editText.getText().toString().trim();
if(text.isEmpty()){
Snackbar.make(btn,
"검색어를 입력하세요.",
Snackbar.LENGTH_SHORT).show();
return;
}
// 네트워크 api를 호출한다.
getNetworkData();
}
});
// 1. 큐를 만든다.
}
// 추가로 20개씩 더 호출할때 사용하는 함수
private void addNetworkData() {
progressBar.setVisibility(View.VISIBLE);
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
// 2. request 만든다.
String url = "https://www.googleapis.com/youtube/v3/search?key="+Config.YOUYUBE_KEY+"&part=snippet&maxResults=20&order=date&q="+text+"&type=video"+"&pageToken"+nextPageToken;
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.GET,
url,
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
progressBar.setVisibility(View.GONE);
try {
nextPageToken = response.getString("nextPageToken");
JSONArray dataArray = response.getJSONArray("items");
for (int i = 0; i < dataArray.length(); i++) {
JSONObject item = dataArray.getJSONObject(i);
JSONObject snippet = item.getJSONObject("snippet");
String title = snippet.getString("title");
String body = snippet.getString("description");
JSONObject thumbnails = snippet.getJSONObject("thumbnails");
JSONObject thumbnail = thumbnails.getJSONObject("default");
JSONObject high = thumbnails.getJSONObject("high");
String url2 = high.getString("url");
String url = thumbnail.getString("url");
JSONObject ids = item.getJSONObject("id");
String id=ids.getString("videoId");
Posting posting = new Posting(title, body, url, id, url2);
postingArrayList.add(posting);
Log.i("MAIN",postingArrayList.toString());
}
adapter = new PostingAdapter(MainActivity.this, postingArrayList);
recyclerView.setAdapter(adapter);
} catch (JSONException e) {
Toast.makeText(MainActivity.this,
"파싱 에러",
Toast.LENGTH_SHORT).show();
Log.i("EMPLOYER MAIN", e.toString());
return;
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
progressBar.setVisibility(View.GONE);
Toast.makeText(MainActivity.this,
"네트워크 통신 에러",
Toast.LENGTH_SHORT).show();
Log.i("EMPLOYER MAIN", error.toString());
}
}
);
// 3. 네트워크로 API 호출
queue.add(request);
}
// 처음 데이터 20개 가져올떄 호출하는 함수.
private void getNetworkData() {
progressBar.setVisibility(View.VISIBLE);
postingArrayList.clear();
RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
// 2. request 만든다.
JsonObjectRequest request = new JsonObjectRequest(
Request.Method.GET,
"https://www.googleapis.com/youtube/v3/search?key="+Config.YOUYUBE_KEY+"&part=snippet&maxResults=20&order=date&q="+text+"&type=video",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
progressBar.setVisibility(View.GONE);
try {
nextPageToken = response.getString("nextPageToken");
JSONArray dataArray = response.getJSONArray("items");
for (int i = 0; i < dataArray.length(); i++) {
JSONObject item = dataArray.getJSONObject(i);
JSONObject snippet = item.getJSONObject("snippet");
String title = snippet.getString("title");
String body = snippet.getString("description");
JSONObject thumbnails = snippet.getJSONObject("thumbnails");
JSONObject thumbnail = thumbnails.getJSONObject("default");
JSONObject high = thumbnails.getJSONObject("high");
String url2 = high.getString("url");
String url = thumbnail.getString("url");
JSONObject ids = item.getJSONObject("id");
String id=ids.getString("videoId");
Posting posting = new Posting(title, body, url, id, url2);
postingArrayList.add(posting);
Log.i("MAIN",postingArrayList.toString());
}
adapter = new PostingAdapter(MainActivity.this, postingArrayList);
recyclerView.setAdapter(adapter);
} catch (JSONException e) {
Toast.makeText(MainActivity.this,
"파싱 에러",
Toast.LENGTH_SHORT).show();
Log.i("EMPLOYER MAIN", e.toString());
return;
}
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
progressBar.setVisibility(View.GONE);
Toast.makeText(MainActivity.this,
"네트워크 통신 에러",
Toast.LENGTH_SHORT).show();
Log.i("EMPLOYER MAIN", error.toString());
}
}
);
// 3. 네트워크로 API 호출
queue.add(request);
}
}
PostringAdapter
package com.example.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.example.youtube.R;
import com.example.youtube.model.Posting;
import com.example.youtube.model.imgActivity;
import java.util.ArrayList;
public class PostingAdapter extends RecyclerView.Adapter<PostingAdapter.ViewHolder> {
Context context;
ArrayList<Posting> postingArrayList;
public PostingAdapter(Context context, ArrayList<Posting> postingArrayList) {
this.context = context;
this.postingArrayList = postingArrayList;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.posting_row, parent, false);
return new PostingAdapter.ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Posting posting = postingArrayList.get(position);
holder.txtTitle.setText(posting.getTitle());
holder.txtBody.setText(posting.getBody());
Glide.with(context)
.load(posting.getUrl())
.into(holder.img);
}
@Override
public int getItemCount() {
return postingArrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView txtTitle;
TextView txtBody;
ImageView img;
CardView cardView;
public ViewHolder(@NonNull View itemView) {
super(itemView);
txtTitle = itemView.findViewById(R.id.txtTitle);
txtBody = itemView.findViewById(R.id.txtBody);
img = itemView.findViewById(R.id.img);
cardView = itemView.findViewById(R.id.cardView);
cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int index = getAdapterPosition();
if (index != RecyclerView.NO_POSITION) {
Posting posting = postingArrayList.get(index);
openWebPage("https://www.youtube.com/watch?v="+posting.getId());
}
}
});
img.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, imgActivity.class);
int index= getAdapterPosition();
Posting posting=postingArrayList.get(index);
intent.putExtra("Posting",posting);
context.startActivity(intent);
}
});
}
}
private void openWebPage(String url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
context.startActivity(intent);
}
}
imgActivity
package com.example.youtube.model;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.bumptech.glide.Glide;
import com.example.youtube.R;
public class imgActivity extends AppCompatActivity {
ImageView img2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_img);
Posting posting = (Posting) getIntent().getSerializableExtra("Posting");
img2 = findViewById(R.id.img2);
Glide.with(imgActivity.this).load(posting.getUrl2()).into(img2);
}
}
Posting
package com.example.youtube.model;
import java.io.Serializable;
public class Posting implements Serializable {
private String title;
private String body;
private String url;
private String id;
private String url2;
public Posting(String title, String body, String url, String id, String url2) {
this.title = title;
this.body = body;
this.url = url;
this.id = id;
this.url2 = url2;
}
public String getTitle() {
return title;
}
public String getBody() {
return body;
}
public String getUrl() {
return url;
}
public String getId(){
return id;
}
public String getUrl2() {
return url2;
}
}
xml 파일
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="409dp"
android:layout_height="729dp"
android:layout_marginStart="1dp"
android:layout_marginTop="1dp"
android:layout_marginEnd="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/search_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="horizontal">
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:ems="10"
android:hint="검색"
android:inputType="text" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:layout_marginRight="0dp"
android:backgroundTint="#FF0000"
android:text="검색"
android:textColor="#000000"
android:textSize="30sp" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="visible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_below="@id/search_layout"
android:layout_alignParentBottom="true"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"/>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
card_view:cardCornerRadius="15dp"
card_view:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/img"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_launcher_background" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:paddingStart="8dp"
android:paddingLeft="8dp">
<TextView
android:id="@+id/txtTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="Title"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txtBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Content"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>