I’m using Retrofit to load data from web api and display data to recyclerview. Here are my layout:
Item of recycler view:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="100"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="10dp"
android:layout_weight="30"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:cardview="http://schemas.android.com/apk/res-auto"
cardview:cardCornerRadius="@dimen/cardview_corner_radius"
>
<ImageView
android:id="@+id/imgExhibit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/img_no_image" />
</android.support.v7.widget.CardView>
<ProgressBar
android:id="@+id/viewProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminateDrawable="@drawable/my_progress"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="70"
android:orientation="vertical"
android:weightSum="100">
<TextView
android:id="@+id/tvExhibitName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="10"
android:text="TRỐNG NHẠC"
android:textAppearance="@style/ExhibitNameMainscreen" />
<TextView
android:id="@+id/tvExhibitDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/ExhibitDescriptionMainscreen"
android:layout_weight="30"
android:ellipsize="end"
android:maxLines="2"
android:text="Trống nhạc là hiện vật do Toà thánh Ngọc Sắc trao tặng"
/>
</LinearLayout>
</LinearLayout>
Model is auto parse by http://www.jsonschema2pojo.org/ from api:
http://demo.museum.vebrary.vn/api/Exhibit/GetPaging?pageindex=1&pagesize=10
public class ExhibitMainScreenModel {
@SerializedName("EXHID")
@Expose
private Integer eXHID;
@SerializedName("EXHIBITNAME")
@Expose
private String eXHIBITNAME;
@SerializedName("DESCRIPTION")
@Expose
private String dESCRIPTION;
public Integer getEXHID() {
return eXHID;
}
public void setEXHID(Integer eXHID) {
this.eXHID = eXHID;
}
public String getEXHIBITNAME() {
return eXHIBITNAME;
}
public void setEXHIBITNAME(String eXHIBITNAME) {
this.eXHIBITNAME = eXHIBITNAME;
}
public String getDESCRIPTION() {
return dESCRIPTION;
}
public void setDESCRIPTION(String dESCRIPTION) {
this.dESCRIPTION = dESCRIPTION;
}
}
and api http://demo.museum.vebrary.vn/api/Exhibit/GetImages?id=4 return one String of image by id
Previously, I used to add attribute String Image to Model because the previous api return both data and image. And I can load it easy to recycler view. But it toke many time loading.
And then, I have 2 api get data and get image independent.
I want to display data before, and UI main don’t block by loading image to image view, I want to can scroll the recycler view while the image view loading (be like facebook app)
Here are Main activity class:
public class MainActivityNew extends AppCompatActivity implements View.OnClickListener {
Toolbar toolbar;
RecyclerView recyclerView;
NavigationView navigationView;
DrawerLayout drawerLayout;
TextView tvTitleToolbar, txtTitleCategory;
//RecyclerView api
private ExhibitMainscreenRecyclerViewAdapter mAdapter;
private ApiService mService;
//ProgressDialog
private ProgressBar viewProgressBar;
private CustomProgressDialogTwo customProgressDialogTwo;
private LoadMoreProgressDialog loadMoreProgressDialog;
//load more
private int indexPage=1;
private int size=10;
private EndlessRecyclerViewScrollListener scrollListener;
//
int id;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_new);
//set transparent stt bar
//StatusBarUtil.setTransparent(this);
addControl();
actionBar();
showDataToRecyclerView();
showProgressDialog();
loadAnswers(indexPage,size);
showIntroMenu();
setPositionTextViewTittleCategogy();
addEvent();
}
public void showErrorMessage() {
Toast.makeText(MainActivityNew.this, "Error loading posts", Toast.LENGTH_SHORT).show();
}
private void showProgressDialog() {
viewProgressBar.setVisibility(View.GONE);
customProgressDialogTwo.show();
}
private void showLoadMoreProgressDialog(){
viewProgressBar.setVisibility(View.GONE);
loadMoreProgressDialog.show();
}
private void loadAnswers(int indexPage,int size) {
new Thread(new Runnable() {
@Override
public void run() {
mService.getExhibitByPage(indexPage,size).enqueue(new Callback<AllExhibitJsonResponse>() {
@Override
public void onResponse(Call<AllExhibitJsonResponse> call, Response<AllExhibitJsonResponse> response) {
if(response.isSuccessful()) {
customProgressDialogTwo.dismiss();
mAdapter.updateAnswers(response.body().getExhibitModels());
Log.d("AnswersPresenter", "posts loaded from API");
}else {
int statusCode = response.code();
Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<AllExhibitJsonResponse> call, Throwable t) {
showErrorMessage();
Log.d("AnswersPresenter", "error loading from API");
}
});
}
}).start();
}
private void showDataToRecyclerView() {
mAdapter = new ExhibitMainscreenRecyclerViewAdapter(this, new ArrayList<ExhibitMainScreenModel>(0), new ExhibitMainscreenRecyclerViewAdapter.PostItemListener() {
@Override
public void onPostClick(long id) {
startDetailActivity((int)id);
}
});
/*RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);*/
GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(mAdapter);
recyclerView.setHasFixedSize(true);
EndlessRecyclerViewScrollListener scrollListener = new EndlessRecyclerViewScrollListener(layoutManager) {
@Override
public void onLoadMore(int page, int totalItemsCount, RecyclerView view) {
showLoadMoreProgressDialog();
loadMoreAnswers(page,size);
}
};
recyclerView.addOnScrollListener(scrollListener);
}
private void loadMoreAnswers(int i, int size) {
new Thread(new Runnable() {
@Override
public void run() {
mService.getExhibitByPage(i+1,size).enqueue(new Callback<AllExhibitJsonResponse>() {
@Override
public void onResponse(Call<AllExhibitJsonResponse> call, Response<AllExhibitJsonResponse> response) {
if(response.isSuccessful()) {
loadMoreProgressDialog.dismiss();
mAdapter.updateMoreAnswers(response.body().getExhibitModels());
Log.d("AnswersPresenter", "posts loaded from API");
}else {
int statusCode = response.code();
Toast.makeText(MainActivityNew.this, "Error"+statusCode+response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<AllExhibitJsonResponse> call, Throwable t) {
showErrorMessage();
Log.d("AnswersPresenter", "error loading from API");
}
});
}
}).start();
}
And the adapter:
public class ExhibitMainscreenRecyclerViewAdapter extends RecyclerView.Adapter<ExhibitMainscreenRecyclerViewAdapter.ViewHolder> {
private List<ExhibitMainScreenModel> ExhibitList;
private Context mContext;
private PostItemListener mItemListener;
private int id;
//web api
private ApiService mService;
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView tvName,tvDescription;
public ImageView imvExhibit;
PostItemListener mItemListener;
public ViewHolder(View itemView, PostItemListener postItemListener) {
super(itemView);
tvName = itemView.findViewById(R.id.tvExhibitName);
tvDescription = itemView.findViewById(R.id.tvExhibitDescription);
imvExhibit=itemView.findViewById(R.id.imgExhibit);
this.mItemListener = postItemListener;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View view) {
ExhibitMainScreenModel item = getItem(getAdapterPosition());
this.mItemListener.onPostClick(item.getEXHID());
notifyDataSetChanged();
}
}
public ExhibitMainscreenRecyclerViewAdapter(Context context, List<ExhibitMainScreenModel> posts, PostItemListener itemListener) {
ExhibitList = posts;
mContext = context;
mItemListener = itemListener;
}
@Override
public ExhibitMainscreenRecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View postView = inflater.inflate(R.layout.item_recyclerview_mainscreen, parent, false);
ViewHolder viewHolder = new ViewHolder(postView, this.mItemListener);
//web api
mService = ApiUtils.getSOService();
return viewHolder;
}
@Override
public void onBindViewHolder(ExhibitMainscreenRecyclerViewAdapter.ViewHolder holder, int position) {
ExhibitMainScreenModel item = ExhibitList.get(position);
TextView tvName = holder.tvName;
tvName.setText(item.getEXHIBITNAME());
TextView tvDesc = holder.tvDescription;
tvDesc.setText(item.getDESCRIPTION());
//
id = item.getEXHID();
loadImage(id,holder);
}
private void loadImage(int id, ViewHolder holder) {
new Thread(new Runnable() {
@Override
public void run() {
//get image default
mService.getExhibitImageById(id, true).enqueue(new Callback<String>() {
@Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
try{
showImage(response.body(),holder);
Log.d("AnswersPresenter", "Image loaded!!!!");
}
catch (Exception e)
{
e.printStackTrace();
}
} else {
int statusCode = response.code();
Toast.makeText(mContext, "Error" + statusCode + response.message(), Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<String> call, Throwable t) {
showErrorMessage();
Log.d("AnswersPresenter", "error loading image!!!");
}
});
}
}).run();
}
@Override
public int getItemCount() {
return ExhibitList.size();
}
public void updateAnswers(List<ExhibitMainScreenModel> items) {
ExhibitList = items;
notifyDataSetChanged();
}
public void updateMoreAnswers(List<ExhibitMainScreenModel> items) {
ExhibitList.addAll(items);
notifyDataSetChanged();
}
private ExhibitMainScreenModel getItem(int adapterPosition) {
return ExhibitList.get(adapterPosition);
}
public interface PostItemListener {
void onPostClick(long id);
}
private void showImage(String imageString, ViewHolder holder) {
Bitmap bmp = Util.StringToBitMap(imageString);
ImageView imv = holder.imvExhibit;
imv.setImageBitmap(bmp);
}
public void showErrorMessage() {
Toast.makeText(mContext, "Error loading posts", Toast.LENGTH_SHORT).show();
}
}
And here is result with my code.
result
but the image is display intricate
So, can you help me?
Ah, It usually have error “android application doing too much work on its main thread”
Can you help me solve it? Thank you very much!!
2
Answers
Once you get the response or url of image the kindly use the libarary by this way it will automatically handle all scenario.
if you are getting url of image in the response in below line:
here if response.body() contains the url of image
Replace
by
if your response is base64 encoded. where urlRes is your encoded response like:-
add two classes in your project.
Base64.java
and second one is FileDecryption.java
If I understood you correctly, you’re using retrofit to load the image from the network. Instead of doing it yourself, there are some few popular libraries intended to handle, load and display images whithin an app, giving the user a free UI thread and experience.
These libraries let you focus on your main logic instead of technical things every developer needs. Also, because they are very popular, they are less buggy than what we all would probably write ourselves and keep on being updated as time goes by.
These libraries include, among others, Picasso, Glide and Fresco.
Good luck!