I am using the reddit API and trying to load the comment tree of a thread. My problem is that I am using a recursive function to do that. I am building a view and then adding it programmatically. And it works for small threads but when it has to load a large comment tree I get stackoverflow.So my main question is: what is a good way to load nested comments programmatically and what is the best practice to go around the stack overflow? I have debated adding a counter for comments added and when they exceed some number I might break the loop, but that still doesn't guarantee me a "stackoverflow" free program. Generally speaking how can I keep track of the stack and the heap? Also as a follow up question: my dynamic view gets destroyed on rotation and I recreate it every time. The problem with that is recreating is slow and it slows the rotation. So is there an easy way to keep/save the view on rotation and add it again (setRetainInstance(true) has no effect on the dynamic view, just on the main layout).
private void addTextTree(JSONObject j, LinearLayout layoutparent) throws JSONException {
JSONObject j2 = j.getJSONObject("data");
JSONArray j3 = j2.getJSONArray("children");
LinearLayout currentparent = layoutparent;
Log.d("ADDINGVIEW", j3.length() + "");
for (int i = 0; i < j3.length(); i++) {
JSONObject j4 = j3.getJSONObject(i);
JSONObject j5 = j4.getJSONObject("data");
addComment(j5, currentparent);
}
}
private void addComment(JSONObject j, LinearLayout parent) throws JSONException {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
LinearLayout LLHmain = new LinearLayout(getActivity());
LinearLayout LLVsecond = new LinearLayout(getActivity());
LLHmain.setOrientation(LinearLayout.HORIZONTAL);
LLHmain.setLayoutParams(lp);
LLVsecond.setOrientation(LinearLayout.VERTICAL);
LLVsecond.setLayoutParams(lp1);
TextView author = new TextView(getActivity());
TextView content = new TextView(getActivity());
ImageView bar = new ImageView(getActivity());
bar.setBackgroundResource(R.drawable.lines);
LLHmain.addView(bar, lp1);
author.setText(" " + j.optString("author") + " " + j.optInt("score") + " points");
author.setBackgroundResource(R.drawable.border);
String temp = j.optString("body_html");
String temp1 = Html.fromHtml(temp.substring(22, temp.length() - 12)).toString();
content.setText(Html.fromHtml(temp1));
content.setAutoLinkMask(Linkify.WEB_URLS);
content.setMovementMethod(LinkMovementMethod.getInstance());
LLVsecond.addView(author, lp);
LLVsecond.addView(content, lp);
LLHmain.addView(LLVsecond, lp1);
parent.addView(LLHmain, lp);
if (!j.optString("replies").equals("")) {
JSONObject replies = j.getJSONObject("replies");
addTextTree(replies, LLVsecond);
}
}
Summed up:
What is the best way to handle loading trees of textviews and preventing stack overflow.
Keeping track of the stack and the heap
Saving dynamic view on rotation
Disclaimer: I am new to any sort of programming and by no means do I believe this is the best or only way to do it. I am open to any possible solutions regarding my problem.
I'll give the very short version of the answer (because I'm kinda busy at work) but you follow it that you'll get what you want:
The issue is that View are memory heavy objects and shouldn't be allocating that many. What you do to avoid that is using a ListView that can recycle views.
So instead of recursively create and add views you'll create a data class, for example:
public class DataItem{
String author;
String content;
}
then you build an ArrayList<DataItem> and recursively add all the items to this array. Then you use this array in an adapter in the listview.
that way you can have a thread with thousands of items without issue.
Related
I am reading data from a SOAP service and then using that data to load images from the res folder locally. I am using a loop because there will always be six images being loaded. This is my code :
final TableLayout tblLay = (TableLayout)findViewById(R.id.lottotl);
final LayoutParams params = new TableLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
for(int i=2;i<9;i++) {
final ImageView image = new ImageView(LastDraw.this);
image.setLayoutParams(trparams);
image.setMaxHeight(20);
image.setMaxWidth(20);
String imgName = "img_" + split[i].substring(split[i].indexOf("=") + 1);
int id = getResources().getIdentifier(imgName, "drawable", getPackageName());
image.setImageResource(id);
row2.setLayoutParams(params);
row2.addView(image);
}
tblLay.addView(row2);
The issue I am having is that there is a gap between the first image and the consecutive images.
It looks like this (each number representing an image):
1.........23456
I am thinking it has to do with the layout of the row in the tablelayout, I could be wrong.
Thank you
Anyone?
Figured it out ... feel kind of stupid but, I learnt something! I load a textview into the first row and then the imageviews into the second row. The textview is in the first column and its width is making the first column stretch ... THAT is why there is a gap.
I have a couple of EditTexts arranged on rows and columns.Those EditTexts contain product name,quantity and price and a TextView that shows the total in real time(calculates it each time you write on one of the EditTexts)
I've setup a a button on each row that when clicked sets visibility of the row(3EditTexts for product name,price and quantity) to GONE.
My problem is that after i set the visibility to GONE,though there are no more EditTexts it still calculates their values from before being GONE.
My question now is,what happens when the EditTexts are set to visibility.GONE ?
My app calculates in real time,so when something happens to an EditText,he calculates again..but it's like the values are still there...Isn't this supposed to be the difference between invisible and gone ?
I'll show you the way i calculate(it is called even after you press the X button to erase the EditTexts,not only when you change values inside EditTexts)
public void calculeaza() {
totaltest = 0;
prod = new String[allprod.size()];
pret = new String[allpret.size()];
cant = new String[allcant.size()];
for (int m = 0; m < allprod.size(); m++) {
prod[m] = allprod.get(m).getText().toString();
if (prod[m].matches("")) {
prod[m] = " - ";
}
}
for (int j = 0; j < allcant.size(); j++) {
cant[j] = allcant.get(j).getText().toString();
if (cant[j].matches("")) {
cant[j] = Float.toString(0);
}
}
for (int k = 0; k < allpret.size(); k++) {
pret[k] = allpret.get(k).getText().toString();
if (pret[k].matches("")) {
pret[k] = Float.toString(0);
}
}
for (int l = 0; l < allpret.size(); l++) {
Float temp = Float.parseFloat(cant[l]) * Float.parseFloat(pret[l]);
totaltest = totaltest + temp;
TextView totalf = (TextView) findViewById(R.id.total);
totalf.setText(String.format("Total: %.2f", totaltest));
}
}
Lines Straight from Android dev site..
View.GONE This view is invisible, and it doesn't take any space for layout purposes.
View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
i.e it retains EditText object even after Gone..
You can reinitialise edittext if you dont want it to retain its value...or setText = ""
Above quoted is the only difference...
Hope this helps...
I'm not really seeing in the code you posted anything I can use to answer this question, but there does appear to be some confusion as to what setVisibility does:
INVISIBLE elements are not seen on the page, but they still take up space (there's a hole where they would be)
GONE elements have no visible effect on the screen, from the user's perspective they aren't there. However they are still part of the view.
If you want to remove the object from the view, then you need to call removeView() on its parent.
It may still take up memory after it has been removed from the view, in case your code has kept references to it in any variables.
It may still take up memory after there are no further references to it, at least until the garbage collector gets around to it.
I'm hoping the rather generalized statements above help clarify the situation.
I have a layout the contains 4 TextViews with ids: "Name1", "Name2", "Name3", "Name4"
I would like to iterate on them with a for loop,
is there any way to do this?
something like
for(int i = 1; i <= 4; i++)
{
findViewById(R.id."Name" + i)
}
I know that this code is far from being real, but any help?
Thank you!
Ron
No, you cannot do it like that because R.id.xyz is referencing a static int of a static class. It's not a string that can be concatenated like that. Also, your code ignores the return value of findViewById so it does nothing (though I realize you mentioned the code is far being real, but still an actual use case might help clarify what you're trying to do). R.id."Name" means nothing and will give you a compiler error.
To loop through you can do something like this:
int[] ids = {R.id.foo, R.id.bar};
then
for(int i = 0; i<ids.length; i++) {
View v = findViewById(ids[i]);
}
Sure you can do that sort of. You cannot access an member of R.id using a string literal you must type out the variable name.
R.id."Test" is no good but R.id.Test is fine.
If you examine the type of R.id.XYZ you will find that it is simply an integer. There is no reason why you cannot perform basic arithmetic on id values.
However it doesn't really make sense to do so. When you build your APK the compiler automatically creates a static class called R that contains references to the various resources and assets contained in your APK, such as layouts, drawables, sounds, etc..
Just because you know that the integer value for R.id.button1 is X there is no guarantee that the integer value for R.id.button2 will be X+1, in fact it could be anything.
If you want to create a list of TextViews to iterate over consider adding them to a List and then iterating over the list. Like so:
ArrayList<TextView> list = new ArrayList<TextView>();
list.add( (TextView) findViewById(R.id.textView1) );
list.add( (TextView) findViewById(R.id.textView2) );
list.add( (TextView) findViewById(R.id.textView3) );
list.add( (TextView) findViewById(R.id.textView4) );
int size = list.getSize();
for (int i=0; i< size; i++)
{
TextView tv = list.get(i);
// Do something with tv like set its label to i
tv.setText(Integer.toString(i));
}
I want an array of imageviews, but I don't know how to fill it with an object of type imageview.
ImageView[] forAdapter = new ImageView[imageIds.size()];
for(int i = 0; i < imageIds.size(); i++)
{
ImageView mImageView = new ImageView(context);
forAdapter[i] = mImageView.setImageDrawable(((imagesPulled.get(imageIds.get(i)))));
}
this doesn't work because .setImageDrawable does not return an imageview, and I dont know of any object that actually does!
I considered using drawables in an array, but I'm ultimately setting an arrayadapter for a listview, and I cant make a R.layout with drawables (I'll get a class cast exception because the xml file is using an ImageView not drawable type),so I only have ImageViews to put into an array - unless I'm approaching the problem wrong.
Your code is almost there! I'm just gonna make a small change and comment on it below:
ImageView[] forAdapter = new ImageView[imageIds.size()];
for(int i = 0; i < imageIds.size(); i++)
{
forAdapter[i] = new ImageView(context);
forAdapter[i].setImageDrawable(imagesPulled.get(imageIds.get(i)));
}
First: If you want to initialize forAdapter[i], you don't need to create a new variable and assign it to it. Just do it there:
forAdapter[i] = new ImageView(context);
Setting the image (using setImageDrawable, setImageResource, etc) doesn't return anything. It's an operation you do on the image itself so all you gotta do is call the method from the variable you want to modify:
forAdapter[i].setImageDrawable(imagesPulled.get(imageIds.get(i)));
You're done :)
If you have any doubts just ask in the comments.
Hope it helps.
Your mImageView is ImageView itself.
So, forAdapter[i] = mImageView should work fine. If you use additional actions, you can do them before assignment.
I am dynamically updating the tablelayout as shown below where I am appending an imagebutton and a Textview for each tableRow.Whenever I launch my activity it shows only 4 rows (should display 10 actually) but if I keep break points in code and debug slowly to figure out the problem it displays all the 10 rows properly. I doubt it must be a memory problem in the code as I am getting Images from the web which I feel takes lot of memory. I tried releasing views memory after adding to the layout but it is crashing all the time.
Please let me know where I am doing wrong.
for (int i = 0; i < parsedExampleDataSet.getAppNameString().size(); i++)
{
TableRow row = new TableRow(this);
TextView tv = new TextView(this);
tv.setText("AppName: "+ parsedExampleDataSet.getAppNameString().get(i) +"\n" + "Description: " + parsedExampleDataSet.getDescriptionString().get(i));
ImageButton imgBtn = new ImageButton(this);
URL aURL = new URL(parsedExampleDataSet.getImageUrlString().get(i));
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
Bitmap bm = BitmapFactory.decodeStream(bis);
imgBtn.setImageBitmap(bm);
imgBtn.setBackgroundColor(color.transparent);
imgBtn.setTag(parsedExampleDataSet.getMarketLinkString().get(i));
bis.close();
is.close();
row.addView(imgBtn);
row.addView(tv);
table.addView(row,new TableLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); //here table is a tablelayout object
}
Thanks in advance
Prathima
If you want to load more than one row than use ListView.
It handles all memory issues and better than TableLayout.
here is one good example.
The answer seems to be located in parsedExampleDataSet.getAppNameString().size():
When you are running the program, the for(;;) loop iterates only 4 times
When you are running the debugger and stepping manually, it iterates 10 times, which is according to your expectation
How is populated parsedExampleDataSet.getAppNameString()? Its size is used as the upper bound of the for(;;) loop, and it seems to change over the time. Should it be populated by an asynchronous process (a thread) this would explain the issue.
Feel free to add more details and let me know. I'll edit/elaborate this answer further to the new elements you'll bring.