I'm currently working on a project which takes pictures from camera and saves the photoPath in a Database, this is working fine and actually I'm handling the memory correctly generating thumbnails when I load all images.
The thing I want is that I'm trying to dedicate threads to go loading images one per one in the layout, because for example, if I have 50, with normal load it shows the layout until all 50 are loaded, meanwhile it doesn't show anything.
I've already tried to implement this threads to load one, and then another one, but it's the same, until it loads all, it shows all, here my code:
String query = "Select id_reg_mem, information from Memory_REG where type = 'PHOTO' and id_memory =" + id_memory;
final Cursor resultado = memoryDB.rawQuery(query, null);
if(resultado.moveToFirst())
noPhotos = resultado.getCount();
for(int i=0; i<noPhotos; i++)
{
new Thread()
{
public void run()
{
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
updateNewPhoto(resultado.getString(1)); // I send the path from the DB to a method I made to add a tablerow with the image (inside an imageview)
}
});
}
}.start();
if(i + 1 == noPhotos)
break;
resultado.moveToNext();
}
Here this method, where I'm loading a single image to an ImageView inside a TableRow. This method is actually working to load all images from DB.
protected void updateNewPhoto(String path)
{
ImageView iv;
if(rows == null) {
rows = new ArrayList<TableRow>();
rows.add(new TableRow(TL.getContext()));
iv = new ImageView(TL.getContext());
iv.setLayoutParams(new TableRow.LayoutParams(250,250));
iv.setTag(DB_Utils.maxIDRegMem(memoryDB) - 1);
iv.setImageBitmap(getBitmapThumbnail(path));
rows.get(0).addView(iv);
TL.addView(rows.get(0));
count = 2;
}
else
{
iv = new ImageView(TL.getContext());
iv.setLayoutParams(new TableRow.LayoutParams(250,250));
iv.setTag(DB_Utils.maxIDRegMem(memoryDB) - 1);
iv.setImageBitmap(getBitmapThumbnail(path));
if(count == 2)
{
rows.get(rows.size() - 1).addView(iv);
count = 1;
}
else
{
rows.add(new TableRow(TL.getContext()));
rows.get(rows.size() - 1).addView(iv);
TL.addView(rows.get(rows.size() - 1));
count = 2;
}
}
Unfortunately for me, all images aren't shown until all of them are loaded (kind of 3 seconds it takes). I'm thinking in no limit of images, that's why I want a way where images be loaded one per one, not all together.
Any ideas you have? I appreciate your help.
Thanks.
In my project I'm going to work with a lot of XML Data like this:
<person>
<name>Alex</name>
<data1>data1</data1>
<data2>data2</data2>
<data3>data3</data3>
</person>
<person>
<name>paul</name>
<data1>data1</data1>
<data2>data2</data2>
<data3>data3</data3>
</person>
Those XML files got like 600+ entrys. I call their sites with fragments and loading them takes pretty long and throws:
"I/Choreographer: Skipped 47 frames! The application may be doing too much work on its main thread."
I already know how to work around that with starting another thread. Still it takes some seconds to open the pages and the "keyword serach" I've implemented causes the Android Monitor to throw
"I/art: Background partial concurrent mark sweep GC freed 83556(6MB) AllocSpace objects, 834(16MB) LOS objects, 45% free, 19MB/35MB, paused 296us total 124.540ms"
like every 3rd time. I've already read about that thats a normal thing happning but should be avoided.
Since I'm still pretty new to Android I wonder if I'm doing it right or if I could handle the Data better using a database for example.
private static String getValue(String tag, Element element) {
NodeList nodeList = element.getElementsByTagName(tag).item(0).getChildNodes();
Node node = nodeList.item(0);
return node.getNodeValue();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
//return inflater.inflate(R.layout.fragment_coffee_pref, container, false);
final View view = inflater.inflate(R.layout.fragment_coffee_pref, container, false);
try {
// get XML file
AssetManager assetManager = getActivity().getAssets();
if(Locale.getDefault().getLanguage().equals("de")){
is = assetManager.open("coffee_pref_DE.xml");
}else{
is = assetManager.open("coffee_pref.xml");
}
//init XML parser
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(is);
Element element=doc.getDocumentElement();
element.normalize();
// init table
LinearLayout CPcontainer = (LinearLayout) view.findViewById(R.id.CPcontainer);
TableRow.LayoutParams w50Layout = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT,0.50f);
TableRow.LayoutParams w100Layout = new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT,1.0f);
//paramsExample.setMargins(2, 2, 2, 2); // (left, top, right, bottom);
// go throught XML List and insert rows for each entry
NodeList nList = doc.getElementsByTagName("person");
for (int i=0; i<nList.getLength(); i++) {
Node node = nList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
TableLayout tableLayout = new TableLayout(getActivity());
tableLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));// assuming the parent view is a LinearLayout
TableRow row1= new TableRow(getActivity());
TableRow row2= new TableRow(getActivity());
TableRow row3= new TableRow(getActivity());
TableRow row4= new TableRow(getActivity());
TableRow.LayoutParams lp = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT);
row1.setLayoutParams(lp);
row2.setLayoutParams(lp);
row3.setLayoutParams(lp);
row4.setLayoutParams(lp);
Element element2 = (Element) node;
// row 1
TextView tvName = new TextView(getActivity());
tvName.setLayoutParams(w100Layout);
tvName.setText(Html.fromHtml(""+getValue("name", element2)+""));
tvName.setMovementMethod(LinkMovementMethod.getInstance());
tvName.setBackgroundResource(R.drawable.head_shape);
tvName.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
row1.addView(tvName);
tableLayout.addView(row1);
// row 2
TextView tvData1 = new TextView(getActivity());
tvData1.setLayoutParams(w50Layout);
tvData1.setText(getValue("data1", element2));
tvData1.setBackgroundResource(R.drawable.cell_shape);
tvData1.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
TextView textData1 = new TextView(getActivity());
textData1.setText(R.string.tableData1);
textData1.setLayoutParams(w50Layout);
//textData1.setTypeface(null, Typeface.BOLD);
textData1.setBackgroundResource(R.drawable.cell_shape);
textData1.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
row2.addView(textData1);
row2.addView(tvData1);
tableLayout.addView(row2);
// row3
TextView tvData2 = new TextView(getActivity());
tvData2.setLayoutParams(w50Layout);
tvData2.setText(getValue("data2", element2));
tvData2.setBackgroundResource(R.drawable.cell_shape);
tvData2.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
TextView textData2 = new TextView(getActivity());
textData2.setText(R.string.tableData2);
textData2.setLayoutParams(w50Layout);
//textData2.setTypeface(null, Typeface.BOLD);
textData2.setBackgroundResource(R.drawable.cell_shape);
textData2.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
row3.addView(textData2);
row3.addView(tvData2);
tableLayout.addView(row3);
// row 4
TextView tvData3 = new TextView(getActivity());
tvData3.setLayoutParams(w50Layout);
tvData3.setText(getValue("sugar", element2));
tvData3.setBackgroundResource(R.drawable.cell_shape);
tvData3.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
TextView textData3 = new TextView(getActivity());
textData3.setText(R.string.tableData3);
textData3.setLayoutParams(w50Layout);
//textData3.setTypeface(null, Typeface.BOLD);
textData3.setBackgroundResource(R.drawable.cell_shape);
textData3.setPadding(30, 20, 30, 20); // (left, top, right, bottom);
row4.addView(textData3);
row4.addView(tvData3);
tableLayout.addView(row4);
// add table to layout
CPcontainer.addView(tableLayout);
}
}
} catch (Exception e) {e.printStackTrace();}
searchforName = (EditText) view.findViewById(R.id.searchForName);
searchforName.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
String searchedName = searchforName.getText().toString().toLowerCase();
LinearLayout CPcontainer = (LinearLayout) view.findViewById(R.id.CPcontainer);
for (int i = 1, j = CPcontainer.getChildCount(); i < j; i++) {
View view2 = CPcontainer.getChildAt(i);
if(view2 instanceof TableLayout) {
TableLayout table2 = (TableLayout) view2;
TableRow row2 = (TableRow) table2.getChildAt(0);
if (!TextUtils.isEmpty(searchedName)) {
TextView searchableName = (TextView) row2.getChildAt(0);
String sName = Html.fromHtml(searchableName.getText().toString()).toString();
if (sName.startsWith(searchedName) || sName.equals(searchedName)) {
//if (sName.toLowerCase().contains(searchedName)) {
table2.setVisibility(View.VISIBLE);
} else {
table2.setVisibility(View.GONE);
}
} else {
table2.setVisibility(View.VISIBLE);
}
}
}
}
});
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
return view;
}
Also since I've implemented a HTML link inside the name TextView I can't search for it anymore and I couldn't figure out how to get around it.
String sName = Html.fromHtml(searchableName.getText().toString()).toString();
I'm gonna be thankful for every help or information about my issues!
XML parsing is just nasty in general for large documents- you create lots of small objects and it causes a lot of havoc in the garbage collector. Libraries that try to minimize object creation can help, but its always going to be bad.
So I wouldn't really worry about the GC warnings. In the end, you aren't going OOM or causing leaks. For the chorographer warning- when parsing large documents do it on another thread or on an AsyncTask. Then the main UI thread can continue to process normally. Send an event to the main thread when the parsing is done to make any UI changes.
Mostly likely you've got a slow DocumentBuilder for parsing your XML. If you have that many entries you have a couple options:
See if you can improve your builder performance by preventing validations, etc. (see this link as one approach: https://jdevel.wordpress.com/2011/03/28/java-documentbuilder-xml-parsing-is-very-slow/). I would recommend wrapping that in code to enable/disable it for debug purposes.
Investigate the differences between DOM and SAX (What is the difference between SAX and DOM?) - you may want and be able to use the SAX approach.
Look to change how your data is organized and/or split it into different files.
Last, XML has it's place but maybe you should use a different file format if performance is the highest priority then document structure/transfer/parsing can be optimized based on you needs better if you build a custom solution (generally this is only for very intense/advanced work and should be avoided if possible).
I am having a problem with a custom view I am currently doing for an app on android, I know there are many questions related with inflaters, but I cannot get around this problem.
the inflater i working just fine, but it should be doing the loop 3 times and is only doing it 1 so I only get one view on my final layout.
the relevant part of the code is this one
void populate(String strcline, String url){
lLfD = (LinearLayout)findViewById(R.id.lLfD);
try{
JSONArray a1 = new JSONArray(strcline);
for(int i = 0; i < a1.length(); i++){
JSONArray a2 = a1.getJSONArray(i);
final String fUserId = a2.getString(0);
String userName = a2.getString(1);
String userPicture = url + a2.getString(2);
View child = getLayoutInflater().inflate(R.layout.cellevery, lLfD);
ImageView avatar = (ImageView)findViewById(R.id.cellAvatar);
downloadFile(userPicture, avatar);
TextView cellName = (TextView)findViewById(R.id.cellName);
cellName.setText(userName);
lLfD.addView(child);
}
}catch(Exception e){
}
pDialog.dismiss();
}
You look like you need to run findViewById only on the inflated view, otherwise it will just find the first one which is only the first one in your loop:
View child = getLayoutInflater().inflate(R.layout.cellevery, lLfD);
ImageView avatar = (ImageView)child.findViewById(R.id.cellAvatar);
downloadFile(userPicture, avatar);
TextView cellName = (TextView)child.findViewById(R.id.cellName);
cellName.setText(userName);
Here's an explanation of findViewById in your loop:
Loop 1:
1LfD->child1->R.id.cellAvatar (findViewById(R.id.cellAvatar) finds this one)
Loop 2:
1Lfd->
child1->R.id.cellAvatar
child2->R.id.cellAvatar (findViewById(R.id.cellAvatar) finds the child1.cellAvatar again)
Loop 3:
1LfD->
child1->R.id.cellAvatar
child2->R.id.cellAvatar
child3->R.id.cellAvatar (findViewById(R.id.cellAvatar) finds the child1.cellAvatar again)
by using child.findViewById(R.id.cellAvatar), it ensures that you find the correct R.id.cellAvatar for each run of the loop.
Does that make sense?
Update 2:
When you call:
getLayoutInflater().inflate(R.layout.cellevery, lLfD);
You are already setting the parent view as the second argument so you don't need to call:
lLfD.addView(child);
I have an xml file which contains the following resources
<LinearLayout android:id="#+id/t214" android:padding="3dip"></LinearLayout>
<LinearLayout android:id="#+id/t215" android:padding="3dip"></LinearLayout>
etc.
I have the following code:
String id = "t" + c.get(Calendar.DAY_OF_WEEK) + c.get(Calendar.HOUR_OF_DAY);
Log.v("calendar", "id string: " + id);
// Get handle of LinearLayout
LinearLayout ll = (LinearLayout) findViewById(CalendarViewActivity.this.getResources().getIdentifier(id, "id", getPackageName()));
According to the log, the id string variable has the value 't214', and therefore getResources().getIdentifier() should return the int identifier so it can be used in findViewById, but ll is being returned as null.
Many thanks!
I am not sure whether I understood the question ... however, the general pattern with findViewById() suggests code like this:
LinearLayout ll = (LinearLayout) findViewById(R.id.t214);
I need to get the coordinates of my app on screen to use them as an offset for a popout.
View tempView = (View) findViewById(R.layout.main);
int[] loc = {0,0};
tempView.getLocationOnScreen(loc); /// crashes here!
Tried this code, but the last line of it makes the app crash. Maybe the tempView I'm getting from the main layout somehow doesn't correspond with the layout on screen?
Any suggestions... thanks! :)
added:
solved
int[] loc = new int[2];
View tempView = (View) findViewById(R.id.LL_whole);
tempView.getLocationOnScreen(loc);
post( String.valueOf(loc[1]) );
works! :)
Try checking to see if tempView is null. I suspect findViewById() is failing to find the id you've given it.
int[] loc = {0,0};
View tempView = (View) findViewById(R.layout.main);
if (tempView != null) {
tempView.getLocationOnScreen(loc);
} else {
Log.d("YourComponent", "tempView was null.");
}
In your XML you have something like this:
<SomeLayout android:id="#+id/myLayout" ... >
<SomeView android:id="#+id/myView" ... />
<SomeOtherView android:id="#+id/myOtherView" .../>
</SomeLayout>
So you want to say:
SomeView v = (SomeView)findViewById(R.id.myView);
Or perhaps:
SomeLayout l = (SomeLayout)findViewById(R.id.myLayout);
findViewById(R.layout.main)
You should place here R.id.something not R.layout.something.