Actualmente estoy trabajando en Android Studio 4.0, Build # AI-193.6911.18.40.6514223.
Al ingresar a la página de noticias, se me muestra una página en blanco. Parece que la API no está recuperando las noticias que se supone que debe. He verificado dos veces mi aplicación completa para ver si hay algún error tipográfico (en caso de que no coincida con los parámetros de la API). Todo parece correcto pero la API no está recuperando los datos.
He adjuntado debajo del código con respecto al problema.
newspage.java:
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.widget.Toast;
import com.example.nvirone.API.ApiClient;
import com.example.nvirone.API.ApiInterface;
import com.example.nvirone.Models.Article;
import com.example.nvirone.Models.News;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class Newspage extends AppCompatActivity {
public static final String API_KEY = "(unique API key)";
private RecyclerView recyclerView;
private Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
private List<Article> articles = new ArrayList<>();
private String TAG = Newspage.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_newspage);
recyclerView = findViewById(R.id.recyclerView);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
adapter = new Adapter(articles, Newspage.this);
recyclerView.setAdapter(adapter);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setNestedScrollingEnabled(false);
LoadJson();
}
public void LoadJson(){
ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
String country = Utils.getCountry();
Call<News> call;
call = apiInterface.getNews(country, API_KEY);
call.enqueue(new Callback<News>() {
@Override
public void onResponse(Call<News> call, Response<News> response) {
if (response.isSuccessful() && response.body().getArticle()!=null){
articles.clear();
articles = response.body().getArticle();
adapter.notifyDataSetChanged();
}
}
@Override
public void onFailure(Call<News> call, Throwable t) {
Toast.makeText(Newspage.this,"NO result",Toast.LENGTH_LONG).show();
}
});
}
}
Newsindetail.java:
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class NewInDetail extends AppCompatActivity {
WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_in_detail);
Intent intent = getIntent();
String url = intent.getStringExtra("url");
webView = findViewById(R.id.webView);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setLoadsImagesAutomatically(true);
webView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl(url);
}
}
Adaptador:
package com.example.nvirone;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import com.example.nvirone.Models.Article;
import com.squareup.picasso.Picasso;
import java.util.List;
public class Adapter extends RecyclerView.Adapter<Adapter.MyViewHolder> {
private List<Article> articles;
private Context context;
private AdapterView.OnItemClickListener OnItemClickListener;
private OnItemClickListener onItemCLickListener;
public Adapter(List<Article> articles, Context context) {
this.articles = articles;
this.context = context;
}
public Adapter() {
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holders, int position) {
final MyViewHolder holder = holders;
final Article model = articles.get(position);
RequestOptions requestOptions = new RequestOptions();
requestOptions.placeholder(Utils.getRandomDrawbleColor());
requestOptions.error(Utils.getRandomDrawbleColor());
requestOptions.diskCacheStrategy(DiskCacheStrategy.ALL);
requestOptions.centerCrop();
Glide.with(context).load(model.getUrlToImage()).apply(requestOptions)
.listener(new RequestListener<Drawable>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
holder.progressBar.setVisibility(View.GONE);
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
holder.progressBar.setVisibility(View.GONE);
return false;
}
})
.transition(DrawableTransitionOptions.withCrossFade())
.into(holder.imageView);
holder.title.setText(model.getTitle());
holder.desc.setText(model.getDescription());
holder.source.setText(model.getSource().getName());
holder.time.setText('\u2022' + Utils.DateToTimeFormat(model.getPublishedAt()));
holder.published.setText(Utils.DateToTimeFormat(model.getPublishedAt()));
holder.author.setText(model.getAuthor());
String imageURL = model.getUrlToImage();
Picasso.get().load(imageURL).into(holder.imageView);
holder.cardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(context,NewInDetail.class);
intent.putExtra("url",model.getUrl());
context.startActivity(intent);
}
});
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item,parent,false);
return new MyViewHolder(view, onItemCLickListener);
}
@Override
public int getItemCount() {
return articles.size();
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemCLickListener = onItemClickListener;
}
public interface OnItemClickListener{
void onItemClick(View view, int position);
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView title, desc, author, published, source, time;
ImageView imageView;
ProgressBar progressBar;
OnItemClickListener onItemClickListener;
CardView cardView;
public MyViewHolder(@NonNull View itemView, OnItemClickListener onItemClickListener) {
super(itemView);
itemView.setOnClickListener(this);
title = itemView.findViewById(R.id.title);
desc = itemView.findViewById(R.id.desc);
author = itemView.findViewById(R.id.author);
published = itemView.findViewById(R.id.publishedAt);
source = itemView.findViewById(R.id.source);
time = itemView.findViewById(R.id.time);
imageView = itemView.findViewById(R.id.img);
progressBar = itemView.findViewById(R.id.progress_load_photo);
cardView = itemView.findViewById(R.id.cardView);
this.onItemClickListener = onItemCLickListener;
}
@Override
public void onClick(View view) {
onItemClickListener.onItemClick(view, getAdapterPosition());
}
}
}
ApiClient:
package com.example.nvirone.API;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
public static final String BaseURL = "https://newsapi.org/v2/";
public static Retrofit retrofit ;
public static Retrofit getApiClient(){
if(retrofit==null){
retrofit = new Retrofit.Builder().baseUrl(BaseURL).client(getUnsafeOkHttpClient().build()).addConverterFactory(GsonConverterFactory.create()).build();
}
return retrofit;
}
public static OkHttpClient.Builder getUnsafeOkHttpClient(){
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return builder;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ApiInterface:
package com.example.nvirone.API;
import com.example.nvirone.Models.News;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface ApiInterface {
@GET("top-headlines")
Call<News> getNews(
@Query("country") String country,
@Query("apiKey") String apiKey
);
}
Utilidades:
package com.example.nvirone;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import org.ocpsoft.prettytime.PrettyTime;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
public class Utils {
public static ColorDrawable[] vibrantLightColorList =
{
new ColorDrawable(Color.parseColor("#ffeead")),
new ColorDrawable(Color.parseColor("#93cfb3")),
new ColorDrawable(Color.parseColor("#fd7a7a")),
new ColorDrawable(Color.parseColor("#faca5f")),
new ColorDrawable(Color.parseColor("#1ba798")),
new ColorDrawable(Color.parseColor("#6aa9ae")),
new ColorDrawable(Color.parseColor("#ffbf27")),
new ColorDrawable(Color.parseColor("#d93947"))
};
public static ColorDrawable getRandomDrawbleColor() {
int idx = new Random().nextInt(vibrantLightColorList.length);
return vibrantLightColorList[idx];
}
public static String DateToTimeFormat(String oldstringDate){
PrettyTime p = new PrettyTime(new Locale(getCountry()));
String isTime = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",
Locale.ENGLISH);
Date date = sdf.parse(oldstringDate);
isTime = p.format(date);
} catch (ParseException e) {
e.printStackTrace();
}
return isTime;
}
public static String DateFormat(String oldstringDate){
String newDate;
SimpleDateFormat dateFormat = new SimpleDateFormat("E, d MMM yyyy", new Locale(getCountry()));
try {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",Locale.CHINA).parse(oldstringDate);
assert date != null;
newDate = dateFormat.format(date);
} catch (ParseException e) {
e.printStackTrace();
newDate = oldstringDate;
}
return newDate;
}
public static String getCountry(){
Locale locale = Locale.getDefault();
String country = locale.getCountry();
return country.toLowerCase();
}
}
Parámetros según la API:
NOTICIAS:
package com.example.nvirone.Models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.util.List;
public class News {
@SerializedName("status")
@Expose
private String status;
@SerializedName("totalResults")
@Expose
private int totalResult;
@SerializedName("articles")
@Expose
private List<Article> article;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public int getTotalResult() {
return totalResult;
}
public void setTotalResult(int totalResult) {
this.totalResult = totalResult;
}
public List<Article> getArticle() {
return article;
}
public void setArticle(List<Article> article) {
this.article = article;
}
}
Artículo:
package com.example.nvirone.Models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Article {
@SerializedName("source")
@Expose
private Source source;
@SerializedName("author")
@Expose
private String author;
@SerializedName("title")
@Expose
private String title;
@SerializedName("description")
@Expose
private String description;
@SerializedName("url")
@Expose
private String url;
@SerializedName("urlToImage")
@Expose
private String urlToImage;
@SerializedName("publishedAt")
@Expose
private String publishedAt;
public Source getSource() {
return source;
}
public void setSource(Source source) {
this.source = source;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUrlToImage() {
return urlToImage;
}
public void setUrlToImage(String urlToImage) {
this.urlToImage = urlToImage;
}
public String getPublishedAt() {
return publishedAt;
}
public void setPublishedAt(String publishedAt) {
this.publishedAt = publishedAt;
}
}
Fuente:
package com.example.nvirone.Models;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Source {
@SerializedName("id")
@Expose
private String id;
@SerializedName("name")
@Expose
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
La vista de tarjeta que se supone que muestra el suministro de noticias:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.cardview.widget.CardView
android:id="@+id/cardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="11dp"
android:layout_marginRight="11dp"
android:layout_marginTop="7dp"
android:layout_marginBottom="7dp"
app:cardElevation="@dimen/cardview_default_elevation"
app:cardCornerRadius="15dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="@+id/img"
android:scaleType="centerCrop"
android:transitionName="img"
tools:ignore= "UnusedAttribute"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="80dp"
android:id="@+id/shadow_bottom"
android:src="@drawable/bottom_shadow"
android:layout_alignBottom="@+id/img" />
<ProgressBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Small"
android:id="@+id/progress_load_photo"
android:layout_marginTop="70dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:id="@+id/author"
android:drawablePadding="10dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/white"
android:singleLine="true"
android:text="Author"
android:gravity="bottom"
android:layout_marginRight="160dp"
android:layout_alignLeft="@+id/title"
android:layout_alignStart="@+id/title"
android:layout_alignRight="@+id/layoutDate"
android:layout_alignTop="@+id/layoutDate"
android:layout_alignEnd="@+id/layoutDate"/>
<FrameLayout
android:id="@+id/layoutDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/img"
android:background="@drawable/round_white"
android:padding="5dp"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:layout_marginTop="50dp">
<ImageView
android:src="@drawable/ic_date"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#606060"
android:id="@+id/publishedAt"
android:layout_marginLeft="27dp"
android:layout_marginRight="10dp"
android:text="01 January 2000"/>
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-light"
android:textStyle="bold"
android:textColor="@color/colorTextTitle"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:text="Title"
android:textSize="17sp"
android:layout_marginTop="10dp"
android:layout_below="@id/img"
android:id="@+id/title"/>
<TextView
android:id="@+id/desc"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginRight="16dp"
android:layout_below="@+id/title"
android:layout_marginLeft="16dp"
android:layout_marginTop="5dp"
android:text="Desc"/>
<TextView
android:id="@+id/source"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_below="@+id/desc"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:fontFamily="sans-serif-light"
android:textStyle="bold"
android:textColor="@color/colorTextTitle"
android:ellipsize="end"
android:singleLine="true"
android:drawablePadding="10dp"
android:maxLines="1"
android:text="Source"/>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_below="@+id/desc"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_toRightOf="@+id/source"
android:ellipsize="end"
android:singleLine="true"
android:drawablePadding="10dp"
android:maxLines="1"
android:text="Time"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
Necesita agregar este método en su Adapter.java
:
public void addArticles(List<Article> articles) {
this.articles.addAll(articles);
int count = getItemCount();
notifyItemRangeInserted(count, count + articles.size());
}
Y úsalo de esta manera en tu NewsPage.java
:
public void LoadJson() {
ApiInterface apiInterface = ApiClient.getApiClient().create(ApiInterface.class);
String country = Utils.getCountry();
Call<News> call;
call = apiInterface.getNews(country, API_KEY);
call.enqueue(new Callback<News>() {
@Override
public void onResponse(Call<News> call, Response<News> response) {
List<Article> newArticles = response.body().getArticle();
if (response.isSuccessful() && newArticles != null) {
adapter.addArticles(newArticles);
}
}
@Override
public void onFailure(Call<News> call, Throwable t) {
Toast.makeText(NewsPage.this, "NO result", Toast.LENGTH_LONG).show();
}
});
}
Este artículo se recopila de Internet, indique la fuente cuando se vuelva a imprimir.
En caso de infracción, por favor [email protected] Eliminar
Déjame decir algunas palabras