Okay, so I have a JSON array of image URLs. I want to load them into a horizontal scrolling view. This is the way I've gone about it, but nothing gets displayed where the ScrollView should go:
public void run() {
JSONArray photosArray;
caption = new WebView(thisContext);
imageScroller = new HorizontalScrollView(thisContext);
imagesHolder = new LinearLayout(thisContext);
imagesHolder.setOrientation(LinearLayout.HORIZONTAL);
try {
photosArray = new JSONArray(postData.getString("photos"));
for(int i = 0; i < photosArray.length(); i++) {
WebView iV = new WebView(thisContext);
JSONObject thisPhoto = photosArray.getJSONObject(i);
JSONArray sizesArray = new JSONArray(thisPhoto.getString("alt_sizes"));
JSONObject largest = sizesArray.getJSONObject(0);
iV.loadData("<img src=\""+largest.getString("url")+"\" />", "text/html", null);
imagesHolder.addView(iV);
}
imageScroller.addView(imagesHolder);
myPostHolder.addView(imageScroller);
caption.loadData(postData.getString("caption"),"text/html",null);
myPostHolder.addView(caption);
} catch (Exception e) {
e.printStackTrace();
}
}
Note that this is done in a runnable class. Thanks a lot
There are two rules in the Android single thread model.
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
So I don't think you can add views to a viewgroup outside of the main thread. Please see: http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html for a fuller explanation.
Also, you need to make sure you are actually setting the contentview and you may need to call invalidate() on the viewgroup to cause a redraw.
And, you probably should be using a GridView..can't see why you'd use a webview for this.
Related
I am still fighting with a fluent recycler view that loads images without blocking the ui thread too much.
The problem is in the word "recycle" itself.
In my app I have a long list with mp3 files that, ofc, all have different names, different artists and different album covers.
However, they all DO use the same layout.
But can I now recycle the view or cant I?
Since loading the album cover right (also cropping it) took the most resources, I have decided to NOT recycle views with an album cover and ONLY recycle those who have no album cover, like so:
private async Task SetContentAsync(PhotoViewHolder vh, int position)
{
string SongName = "";
string ArtistName = "";
Bitmap bitmap = null;
byte[] data = null;
try
{
reader.SetDataSource(mp3Obj[position].Mp3Uri);
}
catch { }
await Task.Run(() => // cause problems with the reload
{
SongName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyTitle);
ArtistName = reader.ExtractMetadata(MediaMetadataRetriever.MetadataKeyArtist);
data = reader.GetEmbeddedPicture();
if (data != null)
{
try
{
bitmap = BitmapFactory.DecodeByteArray(data, 0, data.Length);
}
catch { }
}
});
((Activity)ctx).RunOnUiThread(() =>
{
vh.SongName.SetTypeface(tf, TypefaceStyle.Normal);
vh.AristName.SetTypeface(tf, TypefaceStyle.Normal);
vh.SongName.Text = SongName;
vh.AristName.Text = ArtistName;
try
{
if (bitmap != null)
{
vh.IsRecyclable = false; // MOST IMPORTANT THING TO DO!
ConvertBitmapToBackground(bitmap, vh, false); // Set As Backgorund, blurry and black ( just sets the variable)
CutImageIntoForm(bitmap, vh); // Set as the musical note button
}
else // because recycler items inherit their shit and if it is altered it just shows views were there shouldnt be any ...
{
vh.IsRecyclable = true;
// vh.CoverArt.SetImageResource(Resource.Drawable.btn_musicalnote);
// ConvertBitmapToBackground(bitmap, vh, true); // Set As Backgorund, blurry and black ( just sets the variable)
}
}
catch { }
});
}
Notice how I say "vh.IsRecyclable = ..." depending on whether there is an album cover or not?
This was the solution with the least amount of lags and works when only a few views include an album cover art. However, when the user has all songs with album covers the recycler view becomes extremely slow.
Another issue I am having is that sometimes, when lag occurs, the views double or even tripple. The still lead to the right file, but they all have the same song name for instance.
So what is best practice?
When can I recycle a view and when not?
How can I speed up the recycler view?
Thanks a lot for any answer!
My application generates ListFragments based on user type (determined by the DeviceID) and then fetch images from a web server. These images will then be displayed as a list.
My question is how can I cache those images as well as fragments (generated dynamically) to be displayed in offline mode. For instance when user opens the application without having an active internet connection, it should display the images within the fragments generated dynamically last time.
At the moment my app just download the images from a web service each time.
code for generating fragments dynamically in the MainActivity each time when the application loads.
//generating the views based on JSON data
try {
JSONObject resultObject = new JSONObject(result.toString());
boolean success = resultObject.getBoolean("success");
JSONArray jArray = resultObject.getJSONArray("data");
if (success == true) {
//save menu
for (int i = 0; i < jArray.length(); i++) {
postObject = jArray.getJSONObject(i);
if (postObject.has("ev_count")) {
categoriesSet.put("Events", "Events");
mTabsAdapter.addTab(bar.newTab().setText("Events"), EventsFragment.class, null);
}
if (postObject.has("pl_count")) {
categoriesSet.put("Places", "Places");
mTabsAdapter.addTab(bar.newTab().setText("Places"), PlacesFragment.class, null);
}
if (postObject.has("gn_count")) {
categoriesSet.put("General", "General");
mTabsAdapter.addTab(bar.newTab().setText("General"), GeneralFragment.class, null);
}
}
}
//saving values to the shared preferences (hashmap as a string)
categoriesPreferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = categoriesPreferences.edit();
editor.putString("categories", categoriesSet.toString());
editor.commit();
}catch (JSONException e) {
Log.e("ALLOCATE_DAT_ERROR", e.toString());
}
I would just advice to use some image downloading library which also often handles caching for you..
Here is a short list of some of them:
Volley - directly from google, its more like a whole networking stack, but it allows you to easily download Images (see NetworkImageView) and also to cache them (you need to provide a cache implemtantion - google will help you)
Picasso - nice library from square with very straightforward API - I would advice you to go with it, it might be the easiest way to go
Universal Image Loader - another option, it has really a lot of options and is also easy to use
Here is a simple sample code which should advice you how to use volley to load and cache images.
NetworkImageView mage = (NetworkImageView) view.findViewById(...);
image.setImageUrl("http://someurl.com/image.png",mImageLoader);
You need an ImageLoader instance then..
public ImageLoader getImageLoader() {
if (mImageLoader == null) {
mImageLoader = new ImageLoader(getImageRequestQueue(), new DiskBitmapCache(getCacheDir(), 50 * 1024 * 1024));
}
return mImageLoader;
}
imageRequestQueue is standard queue you should have already initialised somewhere in your app if already using volley for networking stuff
As a DiskCache you can use this
I'm downloading files with my app via an API (this is working perfectly). Later in my app I'm doing a call to my database to get the file info: id, name, location and return it as a HashMap:
HashMap<Integer, ArrayList<String>> imgTitle;
I take the returned HashMap, extract the ArrayList and use that to populate some buttons. the array list holds the filename and file location. When the page loads the names display correctly but the images don't always load or one loads. If I go back and reenter the activity (via a button press) I'll get different images showing up. I've sent my array to the log and the file location is present and correct for all arrays. Why are only some image showing?
for (Map.Entry<Integer, ArrayList<String>> e : imgTitle.entrySet()) {
...
ArrayList<String> catList = e.getValue();
final String catTitle = catList.get(0);
File indexImage = new File(catList.get(1));
// add images
ImageButton imgButton = new ImageButton(this);
imgButton.setImageURI(Uri.fromFile(indexImage));
...
}
The above is inside a method called on the onCreate. My assumption is maybe the setImageURI is too slow? Any ideas?
Prior to using the setImageURI I used
imgButton.setImageResource(R.drawable.test_vehicle);
as a placeholder it was working fine. However, now I have to use a file that was downloaded and saved locally.
You could try to decode a Bitmap from a stream, like this:
File indexImage = new File(catList.get(1));
InputStream imageStream = new FileInputStream(indexImage);
Bitmap backgroundImage = null;
try {
backgroundImage = BitmapFactory.decodeStream(imageStream);
}
catch(Exception e) {
e.printStackTrace();
}
finally {
try {
//Dispose of the temporary resources
imageStream.close();
imageStream = null //So that the stream is deleted on next GC
}
catch(IOException e) {
e.printStackTrace();
}
}
if (backgroundImage != null) {
ImageButton imgButton = new ImageButton(this);
imgButton.setImageBitmap(backgroundImage);
backgroundImage.recycle();
}
Note: This is pure speculation, I haven't tested any of this. It's simply the first solution that came to my mind
I'm working on implementing a website's API in an Android app. There's a part of this API which gives me an array, which contains an array of image URLs. Now, I can iterate through the arrays just fine, and I have written an AsyncTask that transforms image URLs into drawables. But I don't have a good way to get those drawables into the ImageView (which I am creating programatically in the loop):
void createPhotoPost(JSONObject postData, LinearLayout myPostHolder) {
try {
ImageView myImage = new ImageView(myContext);
JSONArray photosArray = new JSONArray(postData.getString("photos"));
for(int i = 0; i < photosArray.length(); i++) {
JSONObject thisPhoto = photosArray.getJSONObject(i);
JSONArray sizesArray = new JSONArray(thisPhoto.getString("alt_sizes"));
JSONObject largest = sizesArray.getJSONObject(0);
new GetPhotoSet(this).execute(largest.getString("url"));
}
myPostHolder.addView(myImage);
} catch (Exception e) {
e.printStackTrace();
}
}
The function I posted above is called from within another loop. As you can see in my code above, the ImageView myImage needs to eventually contain the drawable that is returned by the execute call of GetPhotoSet.execute. Any ideas? Thanks!
Additional details: Since the method where the data is returned by the AsyncTask is outside of the loop where I declared the ImageView myImage, it doesn't know that myImage exists, and it can't reference it (and there are presumably a handful of myImages by this point, from different iterations of the loop)
Nick, that's what onPostExecute() is for. Create your ImageView in onPostExecute() and add it to your Activity from there. If you are looping over a series of images, you can also use publishProgress() to do this. In your example you should move the entire loop to your task's doInBackground().
I am trying to parse HTML in android from a webpage, and since the webpage it not well formed, I get SAXException.
Is there a way to parse HTML in Android?
I just encountered this problem. I tried a few things, but settled on using JSoup. The jar is about 132k, which is a bit big, but if you download the source and take out some of the methods you will not be using, then it is not as big.
=> Good thing about it is that it will handle badly formed HTML
Here's a good example from their site.
File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
//http://jsoup.org/cookbook/input/load-document-from-url
//Document doc = Jsoup.connect("http://example.com/").get();
Element content = doc.getElementById("content");
Elements links = content.getElementsByTag("a");
for (Element link : links) {
String linkHref = link.attr("href");
String linkText = link.text();
}
Have you tried using Html.fromHtml(source)?
I think that class is pretty liberal with respect to source quality (it uses TagSoup internally, which was designed with real-life, bad HTML in mind). It doesn't support all HTML tags though, but it does come with a handler you can implement to react on tags it doesn't understand.
String tmpHtml = "<html>a whole bunch of html stuff</html>";
String htmlTextStr = Html.fromHtml(tmpHtml).toString();
We all know that programming have endless possibilities.There are numbers of solutions available for a single problem so i think all of the above solutions are perfect and may be helpful for someone but for me this one save my day..
So Code goes like this
private void getWebsite() {
new Thread(new Runnable() {
#Override
public void run() {
final StringBuilder builder = new StringBuilder();
try {
Document doc = Jsoup.connect("http://www.ssaurel.com/blog").get();
String title = doc.title();
Elements links = doc.select("a[href]");
builder.append(title).append("\n");
for (Element link : links) {
builder.append("\n").append("Link : ").append(link.attr("href"))
.append("\n").append("Text : ").append(link.text());
}
} catch (IOException e) {
builder.append("Error : ").append(e.getMessage()).append("\n");
}
runOnUiThread(new Runnable() {
#Override
public void run() {
result.setText(builder.toString());
}
});
}
}).start();
}
You just have to call the above function in onCreate Method of your MainActivity
I hope this one is also helpful for you guys.
Also read the original blog at Medium
Maybe you can use WebView, but as you can see in the doc WebView doesn't support javascript and other stuff like widgets by default.
http://developer.android.com/reference/android/webkit/WebView.html
I think that you can enable javascript if you need it.