간단한 뉴스 앱 만들기 #3 - newsapi로 데이터 받아오기
먼저 adapter에 데이터를 보낼 때 뉴스 데이터를 넘겨야 한다.
뉴스 데이터들은 제목, 이미지, 내용을 포함하고 있다.
이를 객체화 해서 보내는 것이 효율적이므로 news data를 저장할 class를 만들어 두자.
package com.example.newstest;
import java.io.Serializable;
public class NewsData implements Serializable {
private String title, urlToImage, content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrlToImage() {
return urlToImage;
}
public void setUrlToImage(String urlToImage) {
this.urlToImage = urlToImage;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
newsapi.org에서 새로 가입을 하고 api를 얻을 수 있다. 해당 api주소를 저장해둔다.
그 주소에 대한 내용을 보면 다음과 같다.
이 내용에 대해서는 json 포스팅을 참고한다.
그리고, 우리는 volley를 통해 서버와 http 통신을 수행할 것이다.
volley
android developer에 따르면, "Volley는 Android 앱의 네트워킹을 더 쉽고, 무엇보다도 더 빠르게 하는 HTTP 라이브러리입니다." 라고 한다.
이를 이용하면 앱에서 서버와 http 통신을 더욱 간결하게 용이하게 할 수 있다.
먼저 앱에서 네트워크에 접속할 수 있도록 permission을 열어주어야 한다.
Manifest.xml에 <uses-permission android:name="android.permission.INTERNET" />
를 추가한다.
그리고 volley를 이용하기 위해 다음을 추가한다.
implementation 'com.android.volley:volley:1.1.1'
RequestQueue를 생성해주어 우리가 요청할 데이터들을 저장해야한다.
여기에 저장을 해두면 이 RequestQueue가 알아서 요청하고 받아온다.
예제 코드를 보면, 다음과 같다.
RequestQueue queue;
queue = Volley.newRequestQueue(this); //context
String url = "";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
//textView.setText("Response is: "+ response.substring(0,500);
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
}
);
// Add the request to the RequestQueue.
queue.add(stringRequest);
newsapi.org에서 보면 request 방식이 GET형식임을 알 수 있다. 우리도 get 형식에 맞추어 보내준다.
url에다 우리가 복사한 api주소를 넣어준다.
데이터를 정상적으로 받아왔을 때 onResponse함수가 작동하고, response 변수에 데이터가 string 형식으로 저장된다.
받아온 데이터가 json형태 이므로, json객체에 넣어 우리가 필요한 데이터들을 추출하자.
위 이미지에서 articles 요소에 우리가 필요한 데이터가 있다.
따라서, 다음과 같이 생성한다.
JSONArray arrayArticles = new JSONObject(response).getJSONArray("articles");
그리고 받아온 데이터들을 NewsData라는 객체에 각각 저장해두고 꺼내써야한다.
이 객체가 여러개 이므로 List라는 자료구조를 이용하자.
List<NewsData> newsList = new ArrayList<NewsData>();
자, 이제 for문을 이용해서 json배열에 있는 요소 하나하나 꺼내오자.
for (int i = 0; i < arrayArticles.length(); i++) {
JSONObject obj = arrayArticles.getJSONObject(i);
NewsData news = new NewsData();
news.setTitle(obj.getString("title"));
news.setUrlToImage(obj.getString("urlToImage"));
news.setContent(obj.getString("description"));
newsList.add(news);
}
그 후 adapter에 적용해주면 된다.
mAdapter = new MyAdapter(newsList, MainActivity.this);
recyclerView.setAdapter(mAdapter);
이 전체 코드가 onCreate함수 내에 있으면 조금 정리하기 힘드니 getNews라는 메소드를 만들어 따로 분리해주자.
전체 코드
package com.example.newstest;
import ...
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager layoutManager;
RequestQueue queue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
recyclerView = findViewById(R.id.my_recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recyclerView.setHasFixedSize(true);
// use a linear layout manager
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
queue = Volley.newRequestQueue(this);
getNews();
}
public void getNews() {
String url = "http://newsapi.org/v2/everything?q=bitcoin&from=2020-12-17&sortBy=publishedAt&apiKey=API_KEY";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
//textView.setText("Response is: "+ response.substring(0,500));
try {
List<NewsData> newsList = new ArrayList<NewsData>();
JSONArray arrayArticles = new JSONObject(response).getJSONArray("articles");
for (int i = 0; i < arrayArticles.length(); i++) {
JSONObject obj = arrayArticles.getJSONObject(i);
NewsData news = new NewsData();
news.setTitle(obj.getString("title"));
news.setUrlToImage(obj.getString("urlToImage"));
news.setContent(obj.getString("description"));
newsList.add(news);
}
// specify an adapter (see also next example)
mAdapter = new MyAdapter(newsList, MainActivity.this);
recyclerView.setAdapter(mAdapter);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
}
이제 어댑터에서 데이터를 받고 각 카드에 데이터들을 뿌려보자.
MyAdapter
위에서 adapter에 List와 context를 보내주었다. 그렇다면 여기서는 이 두가지 요소를 먼저 받아주어야 한다.
private List<NewsData> mDataset;
public MyAdapter(List<NewsData> myDataset, Context context) {
mDataset = myDataset;
Fresco.initialize(context);
//fresco initialize는 기본적으로 이런 데이터 클래스에서 사용 못하고 activity에서 사용해야한다.
//따라서 activity 주소를 받아와서 지정해준다.
//메모리 누수의 가능성이 있어 추천하지 않음
}
- 왜 context를 받아왔는가?
- 우리가 이미지 로드에 사용할 Fresco때문이다.
- fresco initialize는 기본적으로 이런 데이터 클래스에서 사용 못하고 activity에서 사용해야한다.
따라서 activity 주소를 받아와서 지정해준다. - 메모리 누수의 가능성이 있어 추천하지 않음
그리고 viewholder class에 우리가 생성한 이미지, 제목, 내용 요소들의 주소를 받아온다.
public static class MyViewHolder extends RecyclerView.ViewHolder {
public TextView TextView_Title;
public TextView TextView_Content;
public SimpleDraweeView ImageView_title;
public MyViewHolder(LinearLayout v) {
super(v);
TextView_Title = v.findViewById(R.id.TextView_Title);
TextView_Content = v.findViewById(R.id.TextView_Content);
ImageView_title = v.findViewById(R.id.ImageView_Title);
}
}
그 후 onBindViewHolder메소드에서 카드 하나의 디자인을 정해준다.
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
NewsData news = mDataset.get(position);
holder.TextView_Title.setText(news.getTitle());
String content = (news.getContent() == null) ? "-" : news.getContent();
holder.TextView_Content.setText(content);
Uri uri = Uri.parse(news.getUrlToImage());
holder.ImageView_title.setImageURI(uri);
}
fresco 이미지 로딩을 위해 url을 uri로 변환하여 넣어주어야 한다.
따라서, 위와 같이 생성한다.
전체 코드
package com.example.newstest;
import ...
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<NewsData> mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class MyViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView TextView_Title;
public TextView TextView_Content;
public SimpleDraweeView ImageView_title;
public MyViewHolder(LinearLayout v) {
super(v);
TextView_Title = v.findViewById(R.id.TextView_Title);
TextView_Content = v.findViewById(R.id.TextView_Content);
ImageView_title = v.findViewById(R.id.ImageView_Title);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(List<NewsData> myDataset, Context context) {
mDataset = myDataset;
Fresco.initialize(context);
}
// Create new views (invoked by the layout manager)
@Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.row_news, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
NewsData news = mDataset.get(position);
holder.TextView_Title.setText(news.getTitle());
String content = (news.getContent() == null) ? "-" : news.getContent();
holder.TextView_Content.setText(content);
Uri uri = Uri.parse(news.getUrlToImage());
holder.ImageView_title.setImageURI(uri);
}
// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return (mDataset == null) ? 0 : mDataset.size();
}
}
원래 newsapi.org에서 데이터를 받아올 예정이었으나 안되어 네이버 api를 통해 데이터를 받아온다.
전체 코드
//Main Activity
package com.example.newstest;
import ...
public class MainActivity extends AppCompatActivity {
final String clientID = "개인 아이디";
final String clientPassword = "개인 비밀번호";
private RecyclerView recyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager layoutManager;
RequestQueue queue;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_news);
recyclerView = findViewById(R.id.my_recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
recyclerView.setHasFixedSize(true);
// use a linear layout manager
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
queue = Volley.newRequestQueue(this);
getNews();
}
public void getNews() {
String Search = "강아지";
String url = "https://openapi.naver.com/v1/search/book.json?query=" + Search;
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
//textView.setText("Response is: "+ response.substring(0,500));
Log.d("NEWS", response);
try {
List<NewsData> newsList = new ArrayList<NewsData>();
JSONArray arrayArticles = new JSONObject(response).getJSONArray("items");
for (int i = 0; i < arrayArticles.length(); i++) {
JSONObject obj = arrayArticles.getJSONObject(i);
NewsData news = new NewsData();
news.setTitle(obj.getString("title"));
news.setUrlToImage(obj.getString("image"));
news.setContent(obj.getString("description"));
newsList.add(news);
}
// specify an adapter (see also next example)
mAdapter = new MyAdapter(newsList, MainActivity.this);
recyclerView.setAdapter(mAdapter);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
System.out.println(error.getMessage());
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("X-Naver-Client-Id", clientID);
headers.put("X-Naver-Client-Secret", clientPassword);
return headers;
}
};
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
}
Adapter에서는 데이터 그대로 뿌려주면 되므로 위와 동일하다.
실행 화면
'Android > 간단한 뉴스 앱 만들기(기초)' 카테고리의 다른 글
간단한 뉴스 앱 만들기 #5 - 인터넷 브라우저로 열기 (0) | 2021.01.20 |
---|---|
간단한 뉴스 앱 만들기 #4 - 화면 전환 (0) | 2021.01.20 |
간단한 뉴스 앱 만들기 #2 - RecyclerView (0) | 2021.01.17 |
간단한 뉴스 앱 만들기 #1 - UI 생성 (0) | 2021.01.17 |
최근댓글