Actually I'm trying to load images into my reycycleview using glide 4.4.0 and it's loading fine . But now the problem is my recyclerview is lagging a bit when I scroll fast due to the image loading . But I saw some of the glide's method called preload and downloadOnly .So,my question is are these methods helpful for loading image in advance if so then how to use them?
You can use this to fine-tune your preloading. It requires quite a bit of work but looks promising.
In short, you need to create a PreLoader class that will get image URLs from your data set:
private class MyPreloadModelProvider implements PreloadModelProvider {
#Override
#NonNull
public List<U> getPreloadItems(int position) {
String url = myUrls.get(position);
if (TextUtils.isEmpty(url)) {
return Collections.emptyList();
}
return Collections.singletonList(url);
}
#Override
#Nullable
public RequestBuilder getPreloadRequestBuilder(String url) {
return
GlideApp.with(fragment)
.load(url)
.override(imageWidthPixels, imageHeightPixels);
}
}
And then you set it as a scroll listener on your recycler view:
PreloadSizeProvider sizeProvider =
new FixedPreloadSizeProvider(imageWidthPixels, imageHeightPixels);
PreloadModelProvider modelProvider = new MyPreloadModelProvider();
RecyclerViewPreloader<Photo> preloader =
new RecyclerViewPreloader<>(
Glide.with(this), modelProvider, sizeProvider, 10 /*maxPreload*/);
RecyclerView myRecyclerView = (RecyclerView) result.findViewById(R.id.recycler_view);
myRecyclerView.addOnScrollListener(preloader);
After this, you'll get your images preloaded before the onBondViewHolder callback in the adapter, and you'll be able to display them from the cache.
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
ImageView imageView = ((MyViewHolder) viewHolder).imageView;
String currentUrl = myUrls.get(position);
GlideApp.with(fragment)
.load(currentUrl)
.override(imageWidthPixels, imageHeightPixels)
.into(imageView);
}
So I'm making an app that displays captured images. I first save the snapped images in a static ArrayList of String (In the code below: methods.locationPath), and then converts these strings to bitmaps and save them in an ArrayList of Bitmap (In the code below: images).
for (String path : methods.locationPath) {
Bitmap bitmap = BitmapFactory.decodeFile(path);
images.add(bitmap);
}
gr = (GridView) findViewById(R.id.grid);
GridAdapter gridAdapter = new GridAdapter(this, values,images);
gr.setAdapter(gridAdapter);
this method, however, takes too long. Is there a way to make this loop faster with as small changes as possible?
Thanks
Instead of changing the image path into Bitmap object. You can directly pass the path of the Image and on Adapter class set the Image like
Picasso.with(mContext).load(new File(imageUrl)).resize(100,100).into(myViewHolder.imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
}
});
If i had to send a request to get data within onBindViewHolder(), how can i update view after data comes back from server?
what I have now is that I cache the data along with row position, so that next time when user scrolls to that row, I can display info right away.
but there are 2 other issues I don't know how to solve.
I scroll the list to view item at position 10, 11 and 12. I decided to wait for data to come back. do i call notifyDataSetChanged() after? because onBindViewHolder already been called by the time data comes back and view would just remained empty, but I also don't think by calling notifyDataSetChanged() after each request would be a good idea.
I start to view the list at position 0 and keep scrolling to position 10. app sends out request to pull data for position 0 to 10. since the request at 0 is sent out first, more likely it would get back first or at least sooner than position 10, but by that time I'm already viewing the item at position 10. my view would start changing if all requests are back in order, so it would show data for position 0 then keep updating all the way to 10.
is it a bad practice to load data from server as recyclerview scrolls? but by doing this would save me a lot of time, and I guess for user too? because instead of sending all the requests ahead of time, user get to see partial data while other data are being loaded in the background.
Thanks!!!
EDITED
public class TestAdapter extends RecyclerView.Adapter<TestAdapter.ViewHolder> {
private Context ctx;
private ArrayList<Photo> alPhotos = new ArrayList<>();
private HashMap<String, Drawable> hmImages = new HashMap<>();
public TestAdapter(Context ctx, ArrayList<Photo> alPhotos) {
this.ctx = ctx;
this.alPhotos = alPhotos;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_photo_brief, parent, false));
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Photo photo = alPhotos.get(position);
loadRemoteImage(photo.IMG, holder.ivThumb, true);
holder.tvEmail.setText(photo.EMAIL);
}
#Override
public int getItemCount() {
return alPhotos.size();
}
private void loadRemoteImage(final String imgUrl, final ImageView view, final boolean cache) {
if (hmImages.containsKey(imgUrl)) {
view.setImageDrawable(hmImages.get(imgUrl));
} else {
final WeakReference<ImageView> weakView = new WeakReference<>(view);
RequestManager.getManager().add(new Request<>(imgUrl, new DrawableParser(), new RequestCallback<Drawable>() {
#Override
public void onFinished(Request<Drawable> request, Response<Drawable> response) {
if (cache) hmImages.put(imgUrl, response.result);
ImageView view = weakView.get();
if (view != null) {
view.setImageDrawable(response.result);
}
}
#Override
public void onError(Request<Drawable> request, Response<Drawable> response) {
}
#Override
public void onTimeout(Request<Drawable> request, Response<Drawable> response) {
}
})
);
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView ivThumb;
private TextView tvEmail;
public ViewHolder(View itemView) {
super(itemView);
findViews();
}
private void findViews() {
ivThumb = (ImageView) itemView.findViewById(R.id.ivThumb);
tvEmail = (TextView) itemView.findViewById(R.id.tvEmail);
}
}
}
It's a good idea to load data while scrolling, a lot of popular apps do that. I personally use WeakReference in this case. I store a weak reference to the view holder in my model when I start loading data. If the reference is still valid by the time I get the response then it makes sense to update the view. If there is no view holder in memory then it's already been recycled and I don't have to update anything anymore.
When onViewRecycled is called you can clear the weak reference and also consider cancelling the network request (depends on your needs).
Caching works perfect with this model, you just insert this logic before making a network request. Again, this depends on your needs, maybe you don't need caching at all, or maybe your data is rarely updated then it makes sense to always use cache until some event.
In my app I also use EventBus, it helps me with event handling, but it is absolutely fine to just use Android SDK and support library.
You can also add a ScrollListener if you need to differentiate the item behavior depending on whether user scrolls the list right now. E.g. in my app I animate the data if list loaded and user wasn't scrolling it (improves interaction with the user). When user scrolls I load data as is, because it will be too much motion on the screen if I animate data.
I am trying to apply a tutorial on a basic app i am building as a method to learn Android Studio, and part of what i am doing is having a new window ( activity ) with a ListView to view information saved in a CSV file. When i load the main app, it is fine, but when i click on the button that suppose to take me to the new window where the List view is to show the CSV content, the app stops ( shut down ).
Attached, a screen shot of the project layout, and the window where the ListView is.
Following are the codes;
This is the code from the class where the code of the ListView is written, the class name is LogerView, and there is a button at the MainActivity class, the main window for the app, that would call the LogerView;
public class LogerView extends AppCompatActivity {
CSVAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.viewer);
ListView mList = (ListView) findViewById(R.id.mList);
mAdapter = new CSVAdapter(this, -1);
mList.setAdapter(mAdapter);
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
Toast.makeText(v.getContext(), mAdapter.getItem(pos).getListCode(), Toast.LENGTH_SHORT).show();
}
}
);
}
public void toasting(String msg) {
Toast.makeText(LogerView.this, msg, Toast.LENGTH_SHORT).show();
}
public void switchToMain(View v) {
Intent MainActivity = new Intent(this, MainActivity.class);
startActivity(MainActivity);
}
}
This is the Code in the CSVAdapter :
public class CSVAdapter extends ArrayAdapter<fileView>{
Context ctx;
public CSVAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
this.ctx = context;
loadArrayFromFile();
}
#Override
public View getView(final int pos, View convertView, final ViewGroup parent){
TextView mView = (TextView)convertView;
if(null == mView){
mView = new TextView(parent.getContext());
mView.setTextSize(15);
}
mView.setText(getItem(pos).getListCode());
mView.setText(getItem(pos).getListNumber());
mView.setText(getItem(pos).getListRadio());
mView.setText(getItem(pos).getListCheckBox());
mView.setText(getItem(pos).getListNoteText());
mView.setText(getItem(pos).getListTime());
return mView;
}
private void loadArrayFromFile(){
try {
String FILENAME = "entry_log.csv";
InputStream is = ctx.openFileInput(FILENAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = reader.readLine()) != null) {
String[] RowData = line.split("/");
if (RowData.length < 6) {
Log.e("SOME_TAG", "Invalid or empty line! . . .");
continue;
} else {
fileView cur = new fileView();
cur.setListCode(RowData[0]);
cur.setListNumber(RowData[1]);
cur.setListRadio(RowData[2]);
cur.setListCheckBox(RowData[3]);
cur.setListNoteText(RowData[4]);
cur.setListTime(RowData[5]);
this.add(cur);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
I renamed the PltInfo class to fileView, and the Code is:
package net.testerapp.loger;
public class fileView {
private String listCode, listNumber, listRadio, listCheckBox, listNoteText, listTime;
public String getListCode() {
return listCode;
}
public void setListCode(String listCode) {
this.listCode = listCode;
}
public String getListNumber() {
return listNumber;
}
public void setListNumber(String listNumber) {
this.listNumber = listNumber;
}
public String getListRadio() {
return listRadio;
}
public void setListRadio(String listRadio) {
this.listRadio = listRadio;
}
public String getListCheckBox() {
return listCheckBox;
}
public void setListCheckBox(String listCheckBox) {
this.listCheckBox = listCheckBox;
}
public String getListNoteText() {
return listNoteText;
}
public void setListNoteText(String listNoteText) {
this.listNoteText = listNoteText;
}
public String getListTime() {
return listTime;
}
public void setListTime(String listTime) {
this.listTime = listTime;
}
}
The MainActivity Code is as following, and so far this one operate in a perfect way, saving info, clearing text, but when clicked on the view Button which should switch to the PltInfo class to display the ListView, is where the app shut down. ( NO CHANGES TO THIS CODE )*
My CSV file is not located in an Assets folder as the tutorial, i view it through Android Device Monitor on this path emulator/data/data/net.testerapp.loger/files/entry_log.csv
so i created a string FILENAME = entry_log.csv;
and placed it in the code after InputStream is = ctx.openFileInput(FILENAME);
not sure if this is the problem and how to solve it ?
Plus the ListView layout, is not well organized in a way where it would show all the items, i just wanted to first test it, by showing the first item as in the tutorial but did not lunch the window at all in order to go from there reorganizing the layout of the List ... any guidance on how to fix that too will be appreciated.
The link to the tutorial is : https://www.youtube.com/watch?v=S8_HnA7aLd0
** Canceld the logcat entry since i am not getting an error now
[![enter image description here][2]][2]
[![enter image description here][3]][3]
Well, i went over the steps in the Tutorial and i redid it, this time i did not get any Error, but still no response from the app, i was apple to switch to the next window activity from the MainActivity after hitting the view button, but then in hte second window, i get an empty space, without the List, i made sure the list in the layout, and the id of the list match what is in the code but not sure why it is not applying the code !?
I edited the code in the main post, to what i have right now, and attached the current screen shots of the app.
One last thing this is the viewer layout .xml file :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:layout_width="wrap_content"
android:layout_height="235dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:id="#+id/mList" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View File"
android:id="#+id/ViewFileBtn"
android:layout_marginTop="60dp"
android:layout_gravity="center"
android:onClick="ViewFile" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Back"
android:id="#+id/BackBtn"
android:layout_gravity="center|bottom"
android:onClick="switchToMain" />
</LinearLayout>
[enter image description here][4]
I am trying to understand the problem here, and awaiting some help, here are some screen shots i took running the debugger, which i am not really familiar with, thus kind of lost trying to understand the threads.
[![enter image description here][5]][5]
[enter image description here][6]
from the exception:
Caused by: java.lang.ArrayIndexOutOfBoundsException: length=1; index=1
at
net.testerapp.loger.CSVAdapter.loadArrayFromFile(CSVAdapter.java:103)
it says you are trying to read array item at index 1, while the array does not have an item in that position
i.e: the array length is shorter than the position you are trying to read.
to avoid this, check the RowData length before creating the object of PltInfo
in method loadArrayFromFile():
while ((line = reader.readLine()) != null) {
//Split to separate the name from the capital
String[] RowData = line.split(",");
if(RowData.legth<6){
Log.e("SOME_TAG","invalid or empty line! skipping...");
continue;
}else{
//Create a State object for this row's data.
PltInfo cur = new PltInfo();
cur.setEntryCode(RowData[0]);
cur.setEntryNum(RowData[1]);
cur.setSelected(RowData[2]);
cur.setCheckBoxText(RowData[3]);
cur.setNoteText(RowData[4]);
cur.setGetNow(RowData[5]);
//Add the State object to the ArrayList (in this case we are the ArrayList).
this.add(cur);
}
}
I believe the reason for the crash is that you are executing loadArrayFromFile on the main thread, this is a long running process as it involves opening and reading a file. try wrapping loadArrayFromFile in an AsyncTask and it's better to move it out of the Adapter class.
**
EDIT
**
You can implement the AsyncTask like this:
private class OpenFileTask extends AsyncTask<Void, Void, ArrayList<PltInfo>> {
protected Long doInBackground(Void... params) {
ArrayList<PltInfo> data=loadArrayFromFile();
return data;
}
private void loadArrayFromFile(){
try {
ArrayList<PltInfo>data=new ArrayList<PltInfo>();
// Get input stream and Buffered Reader for our data file.
String FILENAME = "entry_log.csv";
InputStream is = ctx.openFileInput(FILENAME);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String line;
//Read each line
while ((line = reader.readLine()) != null) {
//Split to separate the name from the capital
String[] RowData = line.split(",");
//Create a State object for this row's data.
PltInfo cur = new PltInfo();
cur.setEntryCode(RowData[0]);
cur.setEntryNum(RowData[1]);
cur.setSelected(RowData[2]);
cur.setCheckBoxText(RowData[3]);
cur.setNoteText(RowData[4]);
cur.setGetNow(RowData[5]);
//Add the State object to the ArrayList (in this case we are the ArrayList).
data.add(cur);
}
return data;
} catch (IOException e) {
e.printStackTrace();
}
}
protected void onPostExecute(ArrayList<PltInfo> result) {
if(result!=null){
}
}
}
Then pass the data to your adapter like this:
private ArrayList<PltInfo>mData;
public CSVAdapter(Context context, int textViewResourceId,ArrayList<PltInfo> data) {
super(context, textViewResourceId);
//Store a reference to the Context so we can use it to load a file from Assets.
this.ctx = context;
mData=data;
}
then call the async task from your activity like this:
OpenFileTask task=new OpenFileTask();
task.execute();
So, Problem solved, and the CODE is sound ... the file had to be uploaded again, and the if statement as #Yazan mentioned really helped ... Thanks to Freenode #android-dev and a Heor *** Zharf ** who insisted i learn how to use the Debugger and followed with me through the problem it is clear.
Must admit, the outcome of the listview is not what expected, i just need to go over the layout and figure out what is the problem with it.
Hi I have say 100+ list items in the server and getting list items using pagination concept(Every time I am getting 10 list items at a time). Till then I am fine with my coding.
I have one class named A and has listView;
and my POJO is :
public class StreamBean{
private String description;
private String thumbnailUrl;
public String getDescription() {
return mDescription;
}
public void setDescription(String mDescription) {
this.mDescription = mDescription;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
}
I am getting 10 items in the first service call and storing in List<StreamBean> and passing to adapter and showing the results.Till Now I am clear.
Now, I am updating my description in the new class B and Getting back when it is finished editing(In Editing class I am calling service call).
In Class A I am showing updated description using position as
View v = gridView.getChildAt(position-gridView.getFirstVisiblePosition());
if(v==null){
return;
}
ImageView assetThumbnail = (ImageView) v.findViewById(R.id.asset_thumbnail);
TextView description = (TextView) v.findViewById(R.id.tv_description);
if (streamBean.getImageBean() != null
&& !TextUtils.isEmpty(streamBean.getImageBean()
.getThumbnailUrl())){
Glide.with(this)
.load(streamBean.getImageBean()
.getThumbnailUrl())
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(assetThumbnail);
}
if(!TextUtils.isEmpty(streamBean.getDescription())){
description.setText(streamBean.getDescription());
}
Till Now I am fine with my coding.
Now my problems are that:
1) When I am doing editing operation for the same list item, I am getting previous description. How can I get the updated description?
2) When I am getting list using pagination, how can I refresh the listItem and place updated StreamBean into existing one.
May be I am wrong with understanding or questions. Please help me with your answers. Thanks in Advance.